Florian Hahn fbcf8a8cbb
[ConstraintElim] Add (UGE, var, 0) to unsigned system for new vars. (#76262)
The constraint system used for ConstraintElimination assumes all
varibles to be signed. This can cause missed optimization in the
unsigned system, due to missing the information that all variables are
unsigned (non-negative).

Variables can be marked as non-negative by adding Var >= 0 for all
variables. This is done for arguments on ConstraintInfo construction and
after adding new variables. This handles cases like the ones outlined in
https://discourse.llvm.org/t/why-does-llvm-not-perform-range-analysis-on-integer-values/74341

The original example shared above is now handled without this change,
but adding another variable means that instcombine won't be able to
simplify examples like https://godbolt.org/z/hTnra7zdY

Adding the extra variables comes with a slight compile-time increase
https://llvm-compile-time-tracker.com/compare.php?from=7568b36a2bc1a1e496ec29246966ffdfc3a8b87f&to=641a47f0acce7755e340447386013a2e086f03d9&stat=instructions:u

stage1-O3    stage1-ReleaseThinLTO    stage1-ReleaseLTO-g  stage1-O0-g
 +0.04%           +0.07%                   +0.05%           +0.02%
stage2-O3    stage2-O0-g    stage2-clang
  +0.05%         +0.05%        +0.05%

https://github.com/llvm/llvm-project/pull/76262
2023-12-23 15:53:48 +01:00

811 lines
27 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
declare void @use(i1)
define void @test_or_ule(i4 %x, i4 %y, i4 %z, i4 %a) {
; CHECK-LABEL: @test_or_ule(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[OR:%.*]] = or i1 [[C_1]], [[C_2]]
; CHECK-NEXT: br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 [[X]], [[A:%.*]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%or = or i1 %c.1, %c.2
br i1 %or, label %bb1, label %exit
bb1:
%c.3 = icmp ule i4 %x, %z
call void @use(i1 %c.3)
%c.4 = icmp ule i4 %x, %a
call void @use(i1 %c.4)
ret void
exit:
%f.1 = icmp ule i4 %x, %z
call void @use(i1 %f.1)
%c.5 = icmp ule i4 %x, %a
call void @use(i1 %c.5)
%t.1 = icmp ugt i4 %y, %z
call void @use(i1 %t.1)
%t.2 = icmp ugt i4 %x, %y
call void @use(i1 %t.2)
%t.3 = icmp ugt i4 %x, %z
call void @use(i1 %t.3)
ret void
}
; The result of test_or_ule and test_or_select_ule should be same
define void @test_or_select_ule(i4 %x, i4 %y, i4 %z, i4 %a) {
; CHECK-LABEL: @test_or_select_ule(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[OR:%.*]] = select i1 [[C_1]], i1 true, i1 [[C_2]]
; CHECK-NEXT: br i1 [[OR]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: call void @use(i1 [[C_3]])
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 [[X]], [[A:%.*]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%or = select i1 %c.1, i1 true, i1 %c.2
br i1 %or, label %bb1, label %exit
bb1:
%c.3 = icmp ule i4 %x, %z
call void @use(i1 %c.3)
%c.4 = icmp ule i4 %x, %a
call void @use(i1 %c.4)
ret void
exit:
%f.1 = icmp ule i4 %x, %z
call void @use(i1 %f.1)
%c.5 = icmp ule i4 %x, %a
call void @use(i1 %c.5)
%t.1 = icmp ugt i4 %y, %z
call void @use(i1 %t.1)
%t.2 = icmp ugt i4 %x, %y
call void @use(i1 %t.2)
%t.3 = icmp ugt i4 %x, %z
call void @use(i1 %t.3)
ret void
}
define i1 @test_or_chain_ule_1(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
; CHECK-LABEL: @test_or_chain_ule_1(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 2, [[A:%.*]]
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[OR_1]], [[C_3]]
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[C_4]], [[OR_2]]
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: [[C_6:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[C_5]], [[C_6]]
; CHECK-NEXT: [[C_7:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_7]]
; CHECK-NEXT: ret i1 [[RES_2]]
; CHECK: exit:
; CHECK-NEXT: [[RES_3:%.*]] = xor i1 true, true
; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], true
; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_4]], true
; CHECK-NEXT: [[RES_6:%.*]] = xor i1 [[RES_5]], true
; CHECK-NEXT: [[RES_7:%.*]] = xor i1 [[RES_6]], true
; CHECK-NEXT: [[RES_8:%.*]] = xor i1 [[RES_7]], true
; CHECK-NEXT: [[RES_9:%.*]] = xor i1 [[RES_8]], true
; CHECK-NEXT: ret i1 [[RES_9]]
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%c.3 = icmp ule i4 2, %x
%c.4 = icmp ule i4 2, %a
%or.1 = or i1 %c.1, %c.2
%or.2 = or i1 %or.1, %c.3
%or.3 = or i1 %c.4, %or.2
br i1 %or.3, label %bb1, label %exit
bb1:
%c.5 = icmp ule i4 %x, %z
%c.6 = icmp ule i4 %x, %a
%res.1 = xor i1 %c.5, %c.6
%c.7 = icmp ule i4 2, %x
%res.2 = xor i1 %res.1, %c.7
ret i1 %res.2
exit:
%f.1 = icmp ule i4 %x, %z
%f.2 = icmp ule i4 2, %x
%res.3 = xor i1 %f.1, %f.2
%t.1 = icmp ugt i4 %y, %z
%res.4 = xor i1 %res.3, %t.1
%t.2 = icmp ugt i4 %x, %y
%res.5 = xor i1 %res.4, %t.2
%t.3 = icmp ugt i4 %x, %z
%res.6 = xor i1 %res.5, %t.3
%t.4 = icmp ugt i4 2, %a
%res.7 = xor i1 %res.6, %t.4
%c.8 = icmp ule i4 %x, %a
%res.8 = xor i1 %res.7, %c.8
%c.9 = icmp ule i4 %x, %b
%res.9 = xor i1 %res.8, %c.9
ret i1 %res.9
}
; Same as @test_or_chain_ule_1, but with the `or`s ordered differently.
define i1 @test_or_chain_ule_2(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
; CHECK-LABEL: @test_or_chain_ule_2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 2, [[A:%.*]]
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[C_3]], [[C_4]]
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[OR_1]], [[OR_2]]
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: [[C_6:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[C_5]], [[C_6]]
; CHECK-NEXT: [[C_7:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_7]]
; CHECK-NEXT: ret i1 [[RES_2]]
; CHECK: exit:
; CHECK-NEXT: [[RES_3:%.*]] = xor i1 true, true
; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], true
; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_4]], true
; CHECK-NEXT: [[RES_6:%.*]] = xor i1 [[RES_5]], true
; CHECK-NEXT: [[RES_7:%.*]] = xor i1 [[RES_6]], true
; CHECK-NEXT: [[RES_8:%.*]] = xor i1 [[RES_7]], true
; CHECK-NEXT: [[RES_9:%.*]] = xor i1 [[RES_8]], true
; CHECK-NEXT: ret i1 [[RES_9]]
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%c.3 = icmp ule i4 2, %x
%c.4 = icmp ule i4 2, %a
%or.1 = or i1 %c.1, %c.2
%or.2 = or i1 %c.3, %c.4
%or.3 = or i1 %or.1, %or.2
br i1 %or.3, label %bb1, label %exit
bb1:
%c.5 = icmp ule i4 %x, %z
%c.6 = icmp ule i4 %x, %a
%res.1 = xor i1 %c.5, %c.6
%c.7 = icmp ule i4 2, %x
%res.2 = xor i1 %res.1, %c.7
ret i1 %res.2
exit:
%f.1 = icmp ule i4 %x, %z
%f.2 = icmp ule i4 2, %x
%res.3 = xor i1 %f.1, %f.2
%t.1 = icmp ugt i4 %y, %z
%res.4 = xor i1 %res.3, %t.1
%t.2 = icmp ugt i4 %x, %y
%res.5 = xor i1 %res.4, %t.2
%t.3 = icmp ugt i4 %x, %z
%res.6 = xor i1 %res.5, %t.3
%t.4 = icmp ugt i4 2, %a
%res.7 = xor i1 %res.6, %t.4
%c.8 = icmp ule i4 %x, %a
%res.8 = xor i1 %res.7, %c.8
%c.9 = icmp ule i4 %x, %b
%res.9 = xor i1 %res.8, %c.9
ret i1 %res.9
}
declare i1 @cond() readnone
; Test with `or` chain that also contains instructions other than `or` and `icmp`.
define i1 @test_or_chain_with_other_conds_ule(i4 %x, i4 %y, i4 %z, i4 %a, i1 %arg.c) {
; CHECK-LABEL: @test_or_chain_with_other_conds_ule(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[C_3:%.*]] = call i1 @cond()
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[C_3]], [[OR_1]]
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[OR_2]], [[ARG_C:%.*]]
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: [[C_6:%.*]] = icmp ule i4 [[X]], [[A:%.*]]
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[C_5]], [[C_6]]
; CHECK-NEXT: [[C_7:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_7]]
; CHECK-NEXT: ret i1 [[RES_2]]
; CHECK: exit:
; CHECK-NEXT: [[RES_3:%.*]] = xor i1 false, true
; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], true
; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_4]], true
; CHECK-NEXT: [[C_8:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: [[RES_6:%.*]] = xor i1 [[RES_5]], [[C_8]]
; CHECK-NEXT: ret i1 [[RES_6]]
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%c.3 = call i1 @cond()
%or.1 = or i1 %c.1, %c.2
%or.2 = or i1 %c.3, %or.1
%or.3 = or i1 %or.2, %arg.c
br i1 %or.3, label %bb1, label %exit
bb1:
%c.5 = icmp ule i4 %x, %z
%c.6 = icmp ule i4 %x, %a
%res.1 = xor i1 %c.5, %c.6
%c.7 = icmp ule i4 2, %x
%res.2 = xor i1 %res.1, %c.7
ret i1 %res.2
exit:
%f.1 = icmp ule i4 %x, %z
%t.1 = icmp ugt i4 %y, %z
%res.3 = xor i1 %f.1, %t.1
%t.2 = icmp ugt i4 %x, %y
%res.4 = xor i1 %res.3, %t.2
%t.3 = icmp ugt i4 %x, %z
%res.5 = xor i1 %res.4, %t.3
%c.8 = icmp ule i4 %x, %a
%res.6 = xor i1 %res.5, %c.8
ret i1 %res.6
}
define i1 @test_or_chain_with_and_ule(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
; CHECK-LABEL: @test_or_chain_with_and_ule(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[C_1:%.*]] = icmp ule i4 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: [[C_2:%.*]] = icmp ule i4 [[Y]], [[Z:%.*]]
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 2, [[A:%.*]]
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[C_3]], [[C_4]]
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[OR_1]], [[AND_2]]
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i4 [[X]], [[Z]]
; CHECK-NEXT: [[C_6:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: [[RES_1:%.*]] = xor i1 [[C_5]], [[C_6]]
; CHECK-NEXT: [[C_7:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_7]]
; CHECK-NEXT: ret i1 [[RES_2]]
; CHECK: exit:
; CHECK-NEXT: [[RES_3:%.*]] = xor i1 false, true
; CHECK-NEXT: [[RES_4:%.*]] = xor i1 [[RES_3]], true
; CHECK-NEXT: [[RES_5:%.*]] = xor i1 [[RES_4]], true
; CHECK-NEXT: [[C_8:%.*]] = icmp ule i4 [[X]], [[A]]
; CHECK-NEXT: [[RES_6:%.*]] = xor i1 [[RES_5]], [[C_8]]
; CHECK-NEXT: [[C_9:%.*]] = icmp ule i4 [[X]], [[B:%.*]]
; CHECK-NEXT: [[RES_7:%.*]] = xor i1 [[RES_6]], [[C_9]]
; CHECK-NEXT: [[RES_8:%.*]] = xor i1 [[RES_7]], true
; CHECK-NEXT: [[C_11:%.*]] = icmp ugt i4 2, [[A]]
; CHECK-NEXT: [[RES_9:%.*]] = xor i1 [[RES_8]], [[C_11]]
; CHECK-NEXT: ret i1 [[RES_9]]
;
entry:
%c.1 = icmp ule i4 %x, %y
%c.2 = icmp ule i4 %y, %z
%c.3 = icmp ule i4 2, %x
%c.4 = icmp ule i4 2, %a
%or.1 = or i1 %c.1, %c.2
%and.2 = and i1 %c.3, %c.4
%or.3 = or i1 %or.1, %and.2
br i1 %or.3, label %bb1, label %exit
bb1:
%c.5 = icmp ule i4 %x, %z
%c.6 = icmp ule i4 %x, %a
%res.1 = xor i1 %c.5, %c.6
%c.7 = icmp ule i4 2, %x
%res.2 = xor i1 %res.1, %c.7
ret i1 %res.2
exit:
%f.1 = icmp ule i4 %x, %z
%t.1 = icmp ugt i4 %y, %z
%res.3 = xor i1 %f.1, %t.1
%t.2 = icmp ugt i4 %x, %y
%res.4 = xor i1 %res.3, %t.2
%t.3 = icmp ugt i4 %x, %z
%res.5 = xor i1 %res.4, %t.3
%c.8 = icmp ule i4 %x, %a
%res.6 = xor i1 %res.5, %c.8
%c.9 = icmp ule i4 %x, %b
%res.7 = xor i1 %res.6, %c.9
%c.10 = icmp ule i4 2, %x
%res.8 = xor i1 %res.7, %c.10
%c.11 = icmp ugt i4 2, %a
%res.9 = xor i1 %res.8, %c.11
ret i1 %res.9
}
define void @test_or_as_add_ult(i8 %init_val, i8 %high) {
; CHECK-LABEL: @test_or_as_add_ult(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START:%.*]] = shl nuw nsw i8 [[INIT_VAL:%.*]], 2
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[F_0:%.*]] = icmp ult i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_0]])
; CHECK-NEXT: [[I_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: [[F_1:%.*]] = icmp ult i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_1]])
; CHECK-NEXT: [[I_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: [[F_2:%.*]] = icmp ult i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_3:%.*]] = or disjoint i8 [[START]], 3
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_4:%.*]] = or i8 [[START]], 4
; CHECK-NEXT: [[C_4:%.*]] = icmp ult i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: ret void
;
entry:
%start = shl nuw nsw i8 %init_val, 2
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%f.0 = icmp ult i8 %start, %high
call void @use(i1 %f.0)
%i.1 = or disjoint i8 %start, 1
%f.1 = icmp ult i8 %i.1, %high
call void @use(i1 %f.1)
%i.2 = or disjoint i8 %start, 2
%f.2 = icmp ult i8 %i.2, %high
call void @use(i1 %f.2)
ret void
end: ; preds = %entry
%t.0 = icmp ult i8 %start, %high
call void @use(i1 %t.0)
%start.1 = or disjoint i8 %start, 1
%t.1 = icmp ult i8 %start.1, %high
call void @use(i1 %t.1)
%start.2 = or disjoint i8 %start, 2
%t.2 = icmp ult i8 %start.2, %high
call void @use(i1 %t.2)
%start.3 = or disjoint i8 %start, 3
%t.3 = icmp ult i8 %start.3, %high
call void @use(i1 %t.3)
%start.4 = or i8 %start, 4
%c.4 = icmp ult i8 %start.4, %high
call void @use(i1 %c.4)
ret void
}
define void @test_or_as_add_ule(i8 %init_val, i8 %high) {
; CHECK-LABEL: @test_or_as_add_ule(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START:%.*]] = shl nuw nsw i8 [[INIT_VAL:%.*]], 2
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[F_0:%.*]] = icmp ule i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_0]])
; CHECK-NEXT: [[I_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: [[F_1:%.*]] = icmp ule i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_1]])
; CHECK-NEXT: [[I_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: [[F_2:%.*]] = icmp ule i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_3:%.*]] = or disjoint i8 [[START]], 3
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: [[START_4:%.*]] = or i8 [[START]], 4
; CHECK-NEXT: [[T_4:%.*]] = icmp ule i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_4]])
; CHECK-NEXT: [[START_5:%.*]] = or i8 [[START]], 5
; CHECK-NEXT: [[C_5:%.*]] = icmp ule i8 [[START_5]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: ret void
;
entry:
%start = shl nuw nsw i8 %init_val, 2
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%f.0 = icmp ule i8 %start, %high
call void @use(i1 %f.0)
%i.1 = or disjoint i8 %start, 1
%f.1 = icmp ule i8 %i.1, %high
call void @use(i1 %f.1)
%i.2 = or disjoint i8 %start, 2
%f.2 = icmp ule i8 %i.2, %high
call void @use(i1 %f.2)
ret void
end: ; preds = %entry
%t.0 = icmp ule i8 %start, %high
call void @use(i1 %t.0)
%start.1 = or disjoint i8 %start, 1
%t.1 = icmp ule i8 %start.1, %high
call void @use(i1 %t.1)
%start.2 = or disjoint i8 %start, 2
%t.2 = icmp ule i8 %start.2, %high
call void @use(i1 %t.2)
%start.3 = or disjoint i8 %start, 3
%t.3 = icmp ule i8 %start.3, %high
call void @use(i1 %t.3)
%start.4 = or i8 %start, 4
%t.4 = icmp ule i8 %start.4, %high
call void @use(i1 %t.4)
%start.5 = or i8 %start, 5
%c.5 = icmp ule i8 %start.5, %high
call void @use(i1 %c.5)
ret void
}
define void @test_or_as_add_ugt(i8 %init_val, i8 %high) {
; CHECK-LABEL: @test_or_as_add_ugt(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START:%.*]] = shl nuw nsw i8 [[INIT_VAL:%.*]], 2
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[T_0:%.*]] = icmp ugt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_0]])
; CHECK-NEXT: [[I_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp ugt i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[I_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: [[T_2:%.*]] = icmp ugt i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_3:%.*]] = or disjoint i8 [[START]], 3
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_4:%.*]] = or i8 [[START]], 4
; CHECK-NEXT: [[F_4:%.*]] = icmp ugt i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_4]])
; CHECK-NEXT: [[START_5:%.*]] = or i8 [[START]], 5
; CHECK-NEXT: [[C_5:%.*]] = icmp ugt i8 [[START_5]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: ret void
;
entry:
%start = shl nuw nsw i8 %init_val, 2
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%t.0 = icmp ugt i8 %start, %high
call void @use(i1 %t.0)
%i.1 = or disjoint i8 %start, 1
%t.1 = icmp ugt i8 %i.1, %high
call void @use(i1 %t.1)
%i.2 = or disjoint i8 %start, 2
%t.2 = icmp ugt i8 %i.2, %high
call void @use(i1 %t.2)
ret void
end: ; preds = %entry
%f.0 = icmp ugt i8 %start, %high
call void @use(i1 %f.0)
%start.1 = or disjoint i8 %start, 1
%f.1 = icmp ugt i8 %start.1, %high
call void @use(i1 %f.1)
%start.2 = or disjoint i8 %start, 2
%f.2 = icmp ugt i8 %start.2, %high
call void @use(i1 %f.2)
%start.3 = or disjoint i8 %start, 3
%f.3 = icmp ugt i8 %start.3, %high
call void @use(i1 %f.3)
%start.4 = or i8 %start, 4
%f.4 = icmp ugt i8 %start.4, %high
call void @use(i1 %f.4)
%start.5 = or i8 %start, 5
%c.5 = icmp ugt i8 %start.5, %high
call void @use(i1 %c.5)
ret void
}
define void @test_or_as_add_uge(i8 %init_val, i8 %high) {
; CHECK-LABEL: @test_or_as_add_uge(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START:%.*]] = shl nuw nsw i8 [[INIT_VAL:%.*]], 2
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[T_0:%.*]] = icmp ugt i8 [[START]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_0]])
; CHECK-NEXT: [[I_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp uge i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[I_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: [[T_2:%.*]] = icmp uge i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_3:%.*]] = or disjoint i8 [[START]], 3
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_4:%.*]] = or i8 [[START]], 4
; CHECK-NEXT: [[C_4:%.*]] = icmp uge i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_5:%.*]] = or i8 [[START]], 5
; CHECK-NEXT: [[C_5:%.*]] = icmp uge i8 [[START_5]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: ret void
;
entry:
%start = shl nuw nsw i8 %init_val, 2
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%t.0 = icmp ugt i8 %start, %high
call void @use(i1 %t.0)
%i.1 = or disjoint i8 %start, 1
%t.1 = icmp uge i8 %i.1, %high
call void @use(i1 %t.1)
%i.2 = or disjoint i8 %start, 2
%t.2 = icmp uge i8 %i.2, %high
call void @use(i1 %t.2)
ret void
end: ; preds = %entry
%f.0 = icmp ugt i8 %start, %high
call void @use(i1 %f.0)
%start.1 = or disjoint i8 %start, 1
%f.1 = icmp uge i8 %start.1, %high
call void @use(i1 %f.1)
%start.2 = or disjoint i8 %start, 2
%f.2 = icmp uge i8 %start.2, %high
call void @use(i1 %f.2)
%start.3 = or disjoint i8 %start, 3
%f.3 = icmp uge i8 %start.3, %high
call void @use(i1 %f.3)
%start.4 = or i8 %start, 4
%c.4 = icmp uge i8 %start.4, %high
call void @use(i1 %c.4)
%start.5 = or i8 %start, 5
%c.5 = icmp uge i8 %start.5, %high
call void @use(i1 %c.5)
ret void
}
define void @test_not_decompose(i8 %start, i8 %high) {
; CHECK-LABEL: @test_not_decompose(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START:%.*]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[I_1:%.*]] = or i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp uge i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[I_2:%.*]] = or i8 [[START]], 2
; CHECK-NEXT: [[T_2:%.*]] = icmp uge i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: [[START_1:%.*]] = or i8 [[START]], 1
; CHECK-NEXT: [[F_1:%.*]] = icmp uge i8 [[START_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_1]])
; CHECK-NEXT: [[START_2:%.*]] = or i8 [[START]], 2
; CHECK-NEXT: [[F_2:%.*]] = icmp uge i8 [[START_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_2]])
; CHECK-NEXT: [[START_3:%.*]] = or i8 [[START]], 3
; CHECK-NEXT: [[F_3:%.*]] = icmp uge i8 [[START_3]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[F_3]])
; CHECK-NEXT: [[START_4:%.*]] = or i8 [[START]], 4
; CHECK-NEXT: [[C_4:%.*]] = icmp uge i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_5:%.*]] = or i8 [[START]], 5
; CHECK-NEXT: [[C_5:%.*]] = icmp uge i8 [[START_5]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: ret void
;
entry:
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%i.1 = or i8 %start, 1
%t.1 = icmp uge i8 %i.1, %high
call void @use(i1 %t.1)
%i.2 = or i8 %start, 2
%t.2 = icmp uge i8 %i.2, %high
call void @use(i1 %t.2)
ret void
end: ; preds = %entry
%start.1 = or i8 %start, 1
%f.1 = icmp uge i8 %start.1, %high
call void @use(i1 %f.1)
%start.2 = or i8 %start, 2
%f.2 = icmp uge i8 %start.2, %high
call void @use(i1 %f.2)
%start.3 = or i8 %start, 3
%f.3 = icmp uge i8 %start.3, %high
call void @use(i1 %f.3)
%start.4 = or i8 %start, 4
%c.4 = icmp uge i8 %start.4, %high
call void @use(i1 %c.4)
%start.5 = or i8 %start, 5
%c.5 = icmp uge i8 %start.5, %high
call void @use(i1 %c.5)
ret void
}
; Nothing in the IR implies the disjoint flag, but we can still use it
; to decompose into an add.
define void @test_decompose_explicit_disjoint(i8 %start, i8 %high) {
; CHECK-LABEL: @test_decompose_explicit_disjoint(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[START_PLUS_3:%.*]] = add nuw i8 [[START:%.*]], 3
; CHECK-NEXT: [[C_1:%.*]] = icmp uge i8 [[START_PLUS_3]], [[HIGH:%.*]]
; CHECK-NEXT: br i1 [[C_1]], label [[THEN:%.*]], label [[END:%.*]]
; CHECK: then:
; CHECK-NEXT: [[I_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: [[T_1:%.*]] = icmp uge i8 [[I_1]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_1]])
; CHECK-NEXT: [[I_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: [[T_2:%.*]] = icmp uge i8 [[I_2]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[T_2]])
; CHECK-NEXT: ret void
; CHECK: end:
; CHECK-NEXT: [[START_1:%.*]] = or disjoint i8 [[START]], 1
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_2:%.*]] = or disjoint i8 [[START]], 2
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_3:%.*]] = or disjoint i8 [[START]], 3
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[START_4:%.*]] = or disjoint i8 [[START]], 4
; CHECK-NEXT: [[C_4:%.*]] = icmp uge i8 [[START_4]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_4]])
; CHECK-NEXT: [[START_5:%.*]] = or disjoint i8 [[START]], 5
; CHECK-NEXT: [[C_5:%.*]] = icmp uge i8 [[START_5]], [[HIGH]]
; CHECK-NEXT: call void @use(i1 [[C_5]])
; CHECK-NEXT: ret void
;
entry:
%start.plus.3 = add nuw i8 %start, 3
%c.1 = icmp uge i8 %start.plus.3, %high
br i1 %c.1, label %then, label %end
then: ; preds = %entry
%i.1 = or disjoint i8 %start, 1
%t.1 = icmp uge i8 %i.1, %high
call void @use(i1 %t.1)
%i.2 = or disjoint i8 %start, 2
%t.2 = icmp uge i8 %i.2, %high
call void @use(i1 %t.2)
ret void
end: ; preds = %entry
%start.1 = or disjoint i8 %start, 1
%f.1 = icmp uge i8 %start.1, %high
call void @use(i1 %f.1)
%start.2 = or disjoint i8 %start, 2
%f.2 = icmp uge i8 %start.2, %high
call void @use(i1 %f.2)
%start.3 = or disjoint i8 %start, 3
%f.3 = icmp uge i8 %start.3, %high
call void @use(i1 %f.3)
%start.4 = or disjoint i8 %start, 4
%c.4 = icmp uge i8 %start.4, %high
call void @use(i1 %c.4)
%start.5 = or disjoint i8 %start, 5
%c.5 = icmp uge i8 %start.5, %high
call void @use(i1 %c.5)
ret void
}