llvm/llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s

; https://alive2.llvm.org/ce/z/5eCiWi
define i8 @urem_assume(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_assume(
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[N]]
; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP1]], i8 0, i8 [[ADD]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
  %cmp = icmp ult i8 %x, %n
  tail call void @llvm.assume(i1 %cmp)
  %add = add nuw i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
}

; https://alive2.llvm.org/ce/z/MGgtYN
define i8 @urem_assume_without_nuw(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_assume_without_nuw(
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[N]]
; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP1]], i8 0, i8 [[ADD]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
  %cmp = icmp ult i8 %x, %n
  tail call void @llvm.assume(i1 %cmp)
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
}

; Negative test: The assume is false
define i8 @urem_assume_eq(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_assume_eq(
; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], [[N:%.*]]
; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
  %cmp = icmp eq i8 %x, %n
  tail call void @llvm.assume(i1 %cmp)
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
}

; Negative test: The assume is false
define i8 @urem_assume_ne(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_assume_ne(
; CHECK-NEXT:  start:
; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[X:%.*]], [[N:%.*]]
; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
start:
  %cmp = icmp ne i8 %x, %n
  tail call void @llvm.assume(i1 %cmp)
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
}

; Negative test: The add constant is not 1
define i8 @urem_assume_with_unexpected_const(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_assume_with_unexpected_const(
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[X:%.*]], [[N:%.*]]
; CHECK-NEXT:    tail call void @llvm.assume(i1 [[CMP]])
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 2
; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
  %cmp = icmp ult i8 %x, %n
  tail call void @llvm.assume(i1 %cmp)
  %add = add i8 %x, 2            ; Transform only when the constant is 1
  %out = urem i8 %add, %n
  ret i8 %out
}

; https://alive2.llvm.org/ce/z/gNhZ2x
define i8 @urem_without_assume(i8 %arg, i8 %arg2) {
; CHECK-LABEL: @urem_without_assume(
; CHECK-NEXT:    [[X:%.*]] = urem i8 [[ARG:%.*]], [[ARG2:%.*]]
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X]]
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq i8 [[ADD]], [[ARG2]]
; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP1]], i8 0, i8 [[ADD]]
; CHECK-NEXT:    ret i8 [[OUT]]
;
  %x = urem i8 %arg, %arg2
  %add = add i8 %x, 1
  %out = urem i8 %add, %arg2
  ret i8 %out
}

; https://alive2.llvm.org/ce/z/eHkgRa
define i8 @urem_with_dominating_condition(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_with_dominating_condition(
; CHECK-NEXT:  start:
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[COND:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[DOTBB0:%.*]], label [[DOTBB1:%.*]]
; CHECK:       .bb0:
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i8 [[ADD]], [[N]]
; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP0]], i8 0, i8 [[ADD]]
; CHECK-NEXT:    ret i8 [[OUT]]
; CHECK:       .bb1:
; CHECK-NEXT:    ret i8 0
;
start:
  %cond = icmp ult i8 %x, %n
  br i1 %cond, label %.bb0, label %.bb1 ; Should also works for a dominating condition
.bb0:
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
.bb1:
  ret i8 0
}

; Revert the dominating condition and target branch at the same time.
define i8 @urem_with_dominating_condition_false(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_with_dominating_condition_false(
; CHECK-NEXT:  start:
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[COND_NOT:%.*]] = icmp ult i8 [[X_FR]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND_NOT]], label [[DOTBB0:%.*]], label [[DOTBB1:%.*]]
; CHECK:       .bb0:
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X_FR]], 1
; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i8 [[ADD]], [[N]]
; CHECK-NEXT:    [[OUT:%.*]] = select i1 [[TMP0]], i8 0, i8 [[ADD]]
; CHECK-NEXT:    ret i8 [[OUT]]
; CHECK:       .bb1:
; CHECK-NEXT:    ret i8 0
;
start:
  %cond = icmp uge i8 %x, %n
  br i1 %cond, label %.bb1, label %.bb0 ; Swap the branch targets
.bb0:
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
.bb1:
  ret i8 0
}

; Negative test
define noundef i8 @urem_with_opposite_condition(i8 %x, i8 %n) {
; CHECK-LABEL: @urem_with_opposite_condition(
; CHECK-NEXT:    [[COND:%.*]] = icmp ult i8 [[X:%.*]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[DOTBB1:%.*]], label [[DOTBB0:%.*]]
; CHECK:       .bb0:
; CHECK-NEXT:    [[ADD:%.*]] = add i8 [[X]], 1
; CHECK-NEXT:    [[OUT:%.*]] = urem i8 [[ADD]], [[N]]
; CHECK-NEXT:    ret i8 [[OUT]]
; CHECK:       .bb1:
; CHECK-NEXT:    ret i8 0
;
  %cond = icmp ult i8 %x, %n
  br i1 %cond, label %.bb1, label %.bb0 ; Revert the condition
.bb0:
  %add = add i8 %x, 1
  %out = urem i8 %add, %n
  ret i8 %out
.bb1:
  ret i8 0
}

declare void @llvm.assume(i1 noundef)