In addition to calculate hash consistently by swapping SELECT's
operands, we also need to inverse the select pattern favor to match the
original logic.
[EarlyCSE] Equivalent SELECTs should hash equally
DenseMap<SimpleValue> assumes that, if its isEqual method returns true
for two elements, then its getHashValue method must return the same value
for them. This invariant is broken when one SELECT node is a min/max
operation, and the other can be transformed into an equivalent min/max by
inverting its predicate and swapping its operands. This patch fixes an
assertion failure that would occur intermittently while compiling the
following IR:
define i32 @t(i32 %i) {
%cmp = icmp sle i32 0, %i
%twin1 = select i1 %cmp, i32 %i, i32 0
%cmpinv = icmp sgt i32 0, %i
%twin2 = select i1 %cmpinv, i32 0, i32 %i
%sink = add i32 %twin1, %twin2
ret i32 %sink
}
Differential Revision: https://reviews.llvm.org/D86843
1120 lines
40 KiB
LLVM
1120 lines
40 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -S -early-cse -earlycse-debug-hash | FileCheck %s
|
|
; RUN: opt < %s -S -basic-aa -early-cse-memssa | FileCheck %s
|
|
|
|
define void @test1(float %A, float %B, float* %PA, float* %PB) {
|
|
; CHECK-LABEL: @test1(
|
|
; CHECK-NEXT: [[C:%.*]] = fadd float [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: store float [[C]], float* [[PA:%.*]], align 4
|
|
; CHECK-NEXT: store float [[C]], float* [[PB:%.*]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%C = fadd float %A, %B
|
|
store float %C, float* %PA
|
|
%D = fadd float %B, %A
|
|
store float %D, float* %PB
|
|
ret void
|
|
}
|
|
|
|
define void @test2(float %A, float %B, i1* %PA, i1* %PB) {
|
|
; CHECK-LABEL: @test2(
|
|
; CHECK-NEXT: [[C:%.*]] = fcmp oeq float [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PA:%.*]], align 1
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PB:%.*]], align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%C = fcmp oeq float %A, %B
|
|
store i1 %C, i1* %PA
|
|
%D = fcmp oeq float %B, %A
|
|
store i1 %D, i1* %PB
|
|
ret void
|
|
}
|
|
|
|
define void @test3(float %A, float %B, i1* %PA, i1* %PB) {
|
|
; CHECK-LABEL: @test3(
|
|
; CHECK-NEXT: [[C:%.*]] = fcmp uge float [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PA:%.*]], align 1
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PB:%.*]], align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%C = fcmp uge float %A, %B
|
|
store i1 %C, i1* %PA
|
|
%D = fcmp ule float %B, %A
|
|
store i1 %D, i1* %PB
|
|
ret void
|
|
}
|
|
|
|
define void @test4(i32 %A, i32 %B, i1* %PA, i1* %PB) {
|
|
; CHECK-LABEL: @test4(
|
|
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PA:%.*]], align 1
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PB:%.*]], align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%C = icmp eq i32 %A, %B
|
|
store i1 %C, i1* %PA
|
|
%D = icmp eq i32 %B, %A
|
|
store i1 %D, i1* %PB
|
|
ret void
|
|
}
|
|
|
|
define void @test5(i32 %A, i32 %B, i1* %PA, i1* %PB) {
|
|
; CHECK-LABEL: @test5(
|
|
; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PA:%.*]], align 1
|
|
; CHECK-NEXT: store i1 [[C]], i1* [[PB:%.*]], align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%C = icmp sgt i32 %A, %B
|
|
store i1 %C, i1* %PA
|
|
%D = icmp slt i32 %B, %A
|
|
store i1 %D, i1* %PB
|
|
ret void
|
|
}
|
|
|
|
; Test degenerate case of commuted compare of identical comparands.
|
|
|
|
define void @test6(float %f, i1* %p1, i1* %p2) {
|
|
; CHECK-LABEL: @test6(
|
|
; CHECK-NEXT: [[C1:%.*]] = fcmp ult float [[F:%.*]], [[F]]
|
|
; CHECK-NEXT: store i1 [[C1]], i1* [[P1:%.*]], align 1
|
|
; CHECK-NEXT: store i1 [[C1]], i1* [[P2:%.*]], align 1
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%c1 = fcmp ult float %f, %f
|
|
%c2 = fcmp ugt float %f, %f
|
|
store i1 %c1, i1* %p1
|
|
store i1 %c2, i1* %p2
|
|
ret void
|
|
}
|
|
|
|
; Min/max operands may be commuted in the compare and select.
|
|
|
|
define i8 @smin_commute(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smin_commute(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[B]], [[A]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: [[R:%.*]] = mul i8 [[M1]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%cmp1 = icmp slt i8 %a, %b
|
|
%cmp2 = icmp slt i8 %b, %a
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = mul i8 %m1, %m2
|
|
ret i8 %r
|
|
}
|
|
|
|
; Min/max can also have a swapped predicate and select operands.
|
|
|
|
define i1 @smin_swapped(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smin_swapped(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[A]], [[B]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[B]], i8 [[A]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp1 = icmp sgt i8 %a, %b
|
|
%cmp2 = icmp slt i8 %a, %b
|
|
%m1 = select i1 %cmp1, i8 %b, i8 %a
|
|
%m2 = select i1 %cmp2, i8 %a, i8 %b
|
|
%r = icmp eq i8 %m2, %m1
|
|
ret i1 %r
|
|
}
|
|
|
|
; Min/max can also have an inverted predicate and select operands.
|
|
|
|
define i1 @smin_inverted(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smin_inverted(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp1 = icmp slt i8 %a, %b
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = icmp eq i8 %m1, %m2
|
|
ret i1 %r
|
|
}
|
|
|
|
define i8 @smax_commute(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smax_commute(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[B]], [[A]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i8 0
|
|
;
|
|
%cmp1 = icmp sgt i8 %a, %b
|
|
%cmp2 = icmp sgt i8 %b, %a
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = urem i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
define i8 @smax_swapped(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smax_swapped(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[A]], [[B]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[B]], i8 [[A]]
|
|
; CHECK-NEXT: ret i8 1
|
|
;
|
|
%cmp1 = icmp slt i8 %a, %b
|
|
%cmp2 = icmp sgt i8 %a, %b
|
|
%m1 = select i1 %cmp1, i8 %b, i8 %a
|
|
%m2 = select i1 %cmp2, i8 %a, i8 %b
|
|
%r = sdiv i8 %m1, %m2
|
|
ret i8 %r
|
|
}
|
|
|
|
define i1 @smax_inverted(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smax_inverted(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp1 = icmp sgt i8 %a, %b
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = icmp eq i8 %m1, %m2
|
|
ret i1 %r
|
|
}
|
|
|
|
define i8 @umin_commute(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @umin_commute(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i8 [[B]], [[A]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i8 0
|
|
;
|
|
%cmp1 = icmp ult i8 %a, %b
|
|
%cmp2 = icmp ult i8 %b, %a
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = sub i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; Choose a vector type just to show that works.
|
|
|
|
define <2 x i8> @umin_swapped(<2 x i8> %a, <2 x i8> %b) {
|
|
; CHECK-LABEL: @umin_swapped(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt <2 x i8> [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult <2 x i8> [[A]], [[B]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[CMP1]], <2 x i8> [[B]], <2 x i8> [[A]]
|
|
; CHECK-NEXT: ret <2 x i8> zeroinitializer
|
|
;
|
|
%cmp1 = icmp ugt <2 x i8> %a, %b
|
|
%cmp2 = icmp ult <2 x i8> %a, %b
|
|
%m1 = select <2 x i1> %cmp1, <2 x i8> %b, <2 x i8> %a
|
|
%m2 = select <2 x i1> %cmp2, <2 x i8> %a, <2 x i8> %b
|
|
%r = sub <2 x i8> %m2, %m1
|
|
ret <2 x i8> %r
|
|
}
|
|
|
|
define i1 @umin_inverted(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @umin_inverted(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp1 = icmp ult i8 %a, %b
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = icmp eq i8 %m1, %m2
|
|
ret i1 %r
|
|
}
|
|
|
|
define i8 @umax_commute(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @umax_commute(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], [[A]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i8 1
|
|
;
|
|
%cmp1 = icmp ugt i8 %a, %b
|
|
%cmp2 = icmp ugt i8 %b, %a
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = udiv i8 %m1, %m2
|
|
ret i8 %r
|
|
}
|
|
|
|
define i8 @umax_swapped(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @umax_swapped(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[A]], [[B]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[B]], i8 [[A]]
|
|
; CHECK-NEXT: [[R:%.*]] = add i8 [[M1]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%cmp1 = icmp ult i8 %a, %b
|
|
%cmp2 = icmp ugt i8 %a, %b
|
|
%m1 = select i1 %cmp1, i8 %b, i8 %a
|
|
%m2 = select i1 %cmp2, i8 %a, i8 %b
|
|
%r = add i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
define i1 @umax_inverted(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @umax_inverted(
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[B]]
|
|
; CHECK-NEXT: ret i1 true
|
|
;
|
|
%cmp1 = icmp ugt i8 %a, %b
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %b
|
|
%m2 = select i1 %cmp2, i8 %b, i8 %a
|
|
%r = icmp eq i8 %m1, %m2
|
|
ret i1 %r
|
|
}
|
|
|
|
; Min/max may exist with non-canonical operands. Value tracking can match those.
|
|
; But we do not use value tracking, so we expect instcombine will canonicalize
|
|
; this code to a form that allows CSE.
|
|
|
|
define i8 @smax_nsw(i8 %a, i8 %b) {
|
|
; CHECK-LABEL: @smax_nsw(
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], [[B]]
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[SUB]], 0
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 0, i8 [[SUB]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[SUB]], i8 0
|
|
; CHECK-NEXT: [[R:%.*]] = sub i8 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%sub = sub nsw i8 %a, %b
|
|
%cmp1 = icmp slt i8 %a, %b
|
|
%cmp2 = icmp sgt i8 %sub, 0
|
|
%m1 = select i1 %cmp1, i8 0, i8 %sub
|
|
%m2 = select i1 %cmp2, i8 %sub, i8 0
|
|
%r = sub i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; Abs/nabs may exist with non-canonical operands. Value tracking can match those.
|
|
; But we do not use value tracking, so we expect instcombine will canonicalize
|
|
; this code to a form that allows CSE.
|
|
|
|
define i8 @abs_swapped(i8 %a) {
|
|
; CHECK-LABEL: @abs_swapped(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A]], 0
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[A]], 0
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]]
|
|
; CHECK-NEXT: [[R:%.*]] = or i8 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp sgt i8 %a, 0
|
|
%cmp2 = icmp slt i8 %a, 0
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = or i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
define i8 @abs_inverted(i8 %a) {
|
|
; CHECK-LABEL: @abs_inverted(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A]], 0
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: ret i8 [[M1]]
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp sgt i8 %a, 0
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = or i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; Abs/nabs may exist with non-canonical operands. Value tracking can match those.
|
|
; But we do not use value tracking, so we expect instcombine will canonicalize
|
|
; this code to a form that allows CSE.
|
|
|
|
define i8 @nabs_swapped(i8 %a) {
|
|
; CHECK-LABEL: @nabs_swapped(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], 0
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[A]], 0
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i8 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp slt i8 %a, 0
|
|
%cmp2 = icmp sgt i8 %a, 0
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = xor i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
define i8 @nabs_inverted(i8 %a) {
|
|
; CHECK-LABEL: @nabs_inverted(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], 0
|
|
; CHECK-NEXT: [[CMP2:%.*]] = xor i1 [[CMP1]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: ret i8 0
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp slt i8 %a, 0
|
|
%cmp2 = xor i1 %cmp1, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = xor i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; Abs/nabs may exist with non-canonical operands. Value tracking can match those.
|
|
; But we do not use value tracking, so we expect instcombine will canonicalize
|
|
; this code to a form that allows CSE.
|
|
|
|
; compares are different.
|
|
define i8 @abs_different_constants(i8 %a) {
|
|
; CHECK-LABEL: @abs_different_constants(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i8 [[A]], -1
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i8 [[A]], 0
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]]
|
|
; CHECK-NEXT: [[R:%.*]] = or i8 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp sgt i8 %a, -1
|
|
%cmp2 = icmp slt i8 %a, 0
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = or i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; Abs/nabs may exist with non-canonical operands. Value tracking can match those.
|
|
; But we do not use value tracking, so we expect instcombine will canonicalize
|
|
; this code to a form that allows CSE.
|
|
|
|
define i8 @nabs_different_constants(i8 %a) {
|
|
; CHECK-LABEL: @nabs_different_constants(
|
|
; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]]
|
|
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i8 [[A]], 0
|
|
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i8 [[A]], -1
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 [[NEG]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[CMP2]], i8 [[NEG]], i8 [[A]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i8 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i8 [[R]]
|
|
;
|
|
%neg = sub i8 0, %a
|
|
%cmp1 = icmp slt i8 %a, 0
|
|
%cmp2 = icmp sgt i8 %a, -1
|
|
%m1 = select i1 %cmp1, i8 %a, i8 %neg
|
|
%m2 = select i1 %cmp2, i8 %neg, i8 %a
|
|
%r = xor i8 %m2, %m1
|
|
ret i8 %r
|
|
}
|
|
|
|
; https://bugs.llvm.org/show_bug.cgi?id=41101
|
|
; Detect equivalence of selects with commuted operands: 'not' cond.
|
|
|
|
define i32 @select_not_cond(i1 %cond, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_cond(
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[COND:%.*]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%not = xor i1 %cond, -1
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %not, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Detect equivalence of selects with commuted operands: 'not' cond with vector select.
|
|
|
|
define <2 x double> @select_not_cond_commute_vec(<2 x i1> %cond, <2 x double> %t, <2 x double> %f) {
|
|
; CHECK-LABEL: @select_not_cond_commute_vec(
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor <2 x i1> [[COND:%.*]], <i1 true, i1 true>
|
|
; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[COND]], <2 x double> [[T:%.*]], <2 x double> [[F:%.*]]
|
|
; CHECK-NEXT: ret <2 x double> <double 1.000000e+00, double 1.000000e+00>
|
|
;
|
|
%not = xor <2 x i1> %cond, <i1 -1, i1 -1>
|
|
%m1 = select <2 x i1> %cond, <2 x double> %t, <2 x double> %f
|
|
%m2 = select <2 x i1> %not, <2 x double> %f, <2 x double> %t
|
|
%r = fdiv nnan <2 x double> %m1, %m2
|
|
ret <2 x double> %r
|
|
}
|
|
|
|
; Negative test - select ops must be commuted.
|
|
|
|
define i32 @select_not_cond_wrong_select_ops(i1 %cond, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_cond_wrong_select_ops(
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[COND:%.*]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[NOT]], i32 [[T]], i32 [[F]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%not = xor i1 %cond, -1
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - not a 'not'.
|
|
|
|
define i32 @select_not_cond_wrong_cond(i1 %cond, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_cond_wrong_cond(
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND:%.*]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[COND]], i32 [[F]], i32 [[T]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%not = xor i1 %cond, -2
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %not, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Detect equivalence of selects with commuted operands: inverted pred with fcmps.
|
|
|
|
define i32 @select_invert_pred_cond(float %x, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_invert_pred_cond(
|
|
; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X:%.*]], 4.200000e+01
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = fcmp one float [[X]], 4.200000e+01
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%cond = fcmp ueq float %x, 42.0
|
|
%invcond = fcmp one float %x, 42.0
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %invcond, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Detect equivalence of selects with commuted operands: inverted pred with icmps and vectors.
|
|
|
|
define <2 x i32> @select_invert_pred_cond_commute_vec(<2 x i8> %x, <2 x i32> %t, <2 x i32> %f) {
|
|
; CHECK-LABEL: @select_invert_pred_cond_commute_vec(
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp sgt <2 x i8> [[X:%.*]], <i8 42, i8 -1>
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp sle <2 x i8> [[X]], <i8 42, i8 -1>
|
|
; CHECK-NEXT: [[M1:%.*]] = select <2 x i1> [[COND]], <2 x i32> [[T:%.*]], <2 x i32> [[F:%.*]]
|
|
; CHECK-NEXT: ret <2 x i32> zeroinitializer
|
|
;
|
|
%cond = icmp sgt <2 x i8> %x, <i8 42, i8 -1>
|
|
%invcond = icmp sle <2 x i8> %x, <i8 42, i8 -1>
|
|
%m1 = select <2 x i1> %cond, <2 x i32> %t, <2 x i32> %f
|
|
%m2 = select <2 x i1> %invcond, <2 x i32> %f, <2 x i32> %t
|
|
%r = xor <2 x i32> %m1, %m2
|
|
ret <2 x i32> %r
|
|
}
|
|
|
|
; Negative test - select ops must be commuted.
|
|
|
|
define i32 @select_invert_pred_wrong_select_ops(float %x, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_invert_pred_wrong_select_ops(
|
|
; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X:%.*]], 4.200000e+01
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = fcmp one float [[X]], 4.200000e+01
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[F:%.*]], i32 [[T:%.*]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[INVCOND]], i32 [[F]], i32 [[T]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%cond = fcmp ueq float %x, 42.0
|
|
%invcond = fcmp one float %x, 42.0
|
|
%m1 = select i1 %cond, i32 %f, i32 %t
|
|
%m2 = select i1 %invcond, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - not an inverted predicate.
|
|
|
|
define i32 @select_invert_pred_wrong_cond(float %x, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_invert_pred_wrong_cond(
|
|
; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X:%.*]], 4.200000e+01
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = fcmp une float [[X]], 4.200000e+01
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[INVCOND]], i32 [[F]], i32 [[T]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%cond = fcmp ueq float %x, 42.0
|
|
%invcond = fcmp une float %x, 42.0
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %invcond, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - cmp ops must match.
|
|
|
|
define i32 @select_invert_pred_wrong_cmp_ops(float %x, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_invert_pred_wrong_cmp_ops(
|
|
; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X:%.*]], 4.200000e+01
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = fcmp one float [[X]], 4.300000e+01
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[INVCOND]], i32 [[F]], i32 [[T]]
|
|
; CHECK-NEXT: [[R:%.*]] = xor i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%cond = fcmp ueq float %x, 42.0
|
|
%invcond = fcmp one float %x, 43.0
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %invcond, i32 %f, i32 %t
|
|
%r = xor i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; If we have both an inverted predicate and a 'not' op, recognize the double-negation.
|
|
|
|
define i32 @select_not_invert_pred_cond(i8 %x, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_invert_pred_cond(
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp ugt i8 [[X:%.*]], 42
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp ule i8 [[X]], 42
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[INVCOND]], true
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%cond = icmp ugt i8 %x, 42
|
|
%invcond = icmp ule i8 %x, 42
|
|
%not = xor i1 %invcond, -1
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%r = sub i32 %m1, %m2
|
|
ret i32 %r
|
|
}
|
|
|
|
; If we have both an inverted predicate and a 'not' op, recognize the double-negation.
|
|
|
|
define i32 @select_not_invert_pred_cond_commute(i8 %x, i8 %y, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_invert_pred_cond_commute(
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp ule i8 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[INVCOND]], true
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[NOT]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp ugt i8 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%invcond = icmp ule i8 %x, %y
|
|
%not = xor i1 %invcond, -1
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%cond = icmp ugt i8 %x, %y
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%r = sub i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - not an inverted predicate.
|
|
|
|
define i32 @select_not_invert_pred_cond_wrong_pred(i8 %x, i8 %y, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_invert_pred_cond_wrong_pred(
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp ult i8 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[INVCOND]], true
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[NOT]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp ugt i8 [[X]], [[Y]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T]], i32 [[F]]
|
|
; CHECK-NEXT: [[R:%.*]] = sub i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%invcond = icmp ult i8 %x, %y
|
|
%not = xor i1 %invcond, -1
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%cond = icmp ugt i8 %x, %y
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%r = sub i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - cmp ops must match.
|
|
|
|
define i32 @select_not_invert_pred_cond_wrong_cmp_op(i8 %x, i8 %y, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_invert_pred_cond_wrong_cmp_op(
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp ule i8 [[X:%.*]], 42
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[INVCOND]], true
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[NOT]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp ugt i8 [[X]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[T]], i32 [[F]]
|
|
; CHECK-NEXT: [[R:%.*]] = sub i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%invcond = icmp ule i8 %x, 42
|
|
%not = xor i1 %invcond, -1
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%cond = icmp ugt i8 %x, %y
|
|
%m1 = select i1 %cond, i32 %t, i32 %f
|
|
%r = sub i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; Negative test - select ops must be same (and not commuted).
|
|
|
|
define i32 @select_not_invert_pred_cond_wrong_select_op(i8 %x, i8 %y, i32 %t, i32 %f) {
|
|
; CHECK-LABEL: @select_not_invert_pred_cond_wrong_select_op(
|
|
; CHECK-NEXT: [[INVCOND:%.*]] = icmp ule i8 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[NOT:%.*]] = xor i1 [[INVCOND]], true
|
|
; CHECK-NEXT: [[M2:%.*]] = select i1 [[NOT]], i32 [[T:%.*]], i32 [[F:%.*]]
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp ugt i8 [[X]], [[Y]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[COND]], i32 [[F]], i32 [[T]]
|
|
; CHECK-NEXT: [[R:%.*]] = sub i32 [[M2]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
;
|
|
%invcond = icmp ule i8 %x, %y
|
|
%not = xor i1 %invcond, -1
|
|
%m2 = select i1 %not, i32 %t, i32 %f
|
|
%cond = icmp ugt i8 %x, %y
|
|
%m1 = select i1 %cond, i32 %f, i32 %t
|
|
%r = sub i32 %m2, %m1
|
|
ret i32 %r
|
|
}
|
|
|
|
; This test is a reproducer for a bug involving inverted min/max selects
|
|
; hashing differently but comparing as equal. It exhibits such a pair of
|
|
; values, and we run this test with -earlycse-debug-hash which would catch
|
|
; the disagreement and fail if it regressed.
|
|
; EarlyCSE should be able to detect the 2nd redundant `select` and eliminate
|
|
; it.
|
|
define i32 @inverted_max(i32 %i) {
|
|
; CHECK-LABEL: @inverted_max(
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 0, [[I:%.*]]
|
|
; CHECK-NEXT: [[M1:%.*]] = select i1 [[CMP]], i32 [[I]], i32 0
|
|
; CHECK-NEXT: [[CMPINV:%.*]] = icmp sgt i32 0, [[I:%.*]]
|
|
; CHECK-NEXT: [[R:%.*]] = add i32 [[M1]], [[M1]]
|
|
; CHECK-NEXT: ret i32 [[R]]
|
|
%cmp = icmp sle i32 0, %i
|
|
%m1 = select i1 %cmp, i32 %i, i32 0
|
|
%cmpinv = icmp sgt i32 0, %i
|
|
%m2 = select i1 %cmpinv, i32 0, i32 %i
|
|
%r = add i32 %m1, %m2
|
|
ret i32 %r
|
|
}
|
|
|
|
; This test is a reproducer for a bug involving inverted min/max selects
|
|
; hashing differently but comparing as equal. It exhibits such a pair of
|
|
; values, and we run this test with -earlycse-debug-hash which would catch
|
|
; the disagreement and fail if it regressed. This test also includes a
|
|
; negation of each negation to check for the same issue one level deeper.
|
|
define void @not_not_min(i32* %px, i32* %py, i32* %pout) {
|
|
; CHECK-LABEL: @not_not_min(
|
|
; CHECK-NEXT: [[X:%.*]] = load volatile i32, i32* [[PX:%.*]], align 4
|
|
; CHECK-NEXT: [[Y:%.*]] = load volatile i32, i32* [[PY:%.*]], align 4
|
|
; CHECK-NEXT: [[CMPA:%.*]] = icmp slt i32 [[X]], [[Y]]
|
|
; CHECK-NEXT: [[CMPB:%.*]] = xor i1 [[CMPA]], true
|
|
; CHECK-NEXT: [[RA:%.*]] = select i1 [[CMPA]], i32 [[X]], i32 [[Y]]
|
|
; CHECK-NEXT: store volatile i32 [[RA]], i32* [[POUT:%.*]], align 4
|
|
; CHECK-NEXT: store volatile i32 [[RA]], i32* [[POUT]], align 4
|
|
; CHECK-NEXT: store volatile i32 [[RA]], i32* [[POUT]], align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%x = load volatile i32, i32* %px
|
|
%y = load volatile i32, i32* %py
|
|
%cmpa = icmp slt i32 %x, %y
|
|
%cmpb = xor i1 %cmpa, -1
|
|
%cmpc = xor i1 %cmpb, -1
|
|
%ra = select i1 %cmpa, i32 %x, i32 %y
|
|
%rb = select i1 %cmpb, i32 %y, i32 %x
|
|
%rc = select i1 %cmpc, i32 %x, i32 %y
|
|
store volatile i32 %ra, i32* %pout
|
|
store volatile i32 %rb, i32* %pout
|
|
store volatile i32 %rc, i32* %pout
|
|
|
|
ret void
|
|
}
|
|
|
|
; This would cause an assert/crash because we matched
|
|
; a ValueTracking select pattern that required 'nsw'
|
|
; on an operand, but we remove that flag as part of
|
|
; CSE matching/hashing.
|
|
|
|
define void @PR41083_1(i32 %span_left, i32 %clip_left) {
|
|
; CHECK-LABEL: @PR41083_1(
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[CLIP_LEFT:%.*]], [[SPAN_LEFT:%.*]]
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[CLIP_LEFT]], [[SPAN_LEFT]]
|
|
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 0
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
%cmp = icmp sgt i32 %clip_left, %span_left
|
|
%sub = sub nsw i32 %clip_left, %span_left
|
|
%cond = select i1 %cmp, i32 %sub, i32 0
|
|
%cmp83292 = icmp slt i32 %cond, undef
|
|
%sub2 = sub i32 %clip_left, %span_left
|
|
%sel2 = select i1 %cmp, i32 %sub2, i32 0
|
|
ret void
|
|
}
|
|
|
|
; This would cause an assert/crash because we matched
|
|
; a ValueTracking select pattern that required 'nsw'
|
|
; on an operand, but we remove that flag as part of
|
|
; CSE matching/hashing.
|
|
|
|
define i32 @PR41083_2(i32 %p) {
|
|
; CHECK-LABEL: @PR41083_2(
|
|
; CHECK-NEXT: [[S:%.*]] = sub i32 0, [[P:%.*]]
|
|
; CHECK-NEXT: [[A:%.*]] = ashr exact i32 [[S]], 2
|
|
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 0, [[A]]
|
|
; CHECK-NEXT: [[SUB:%.*]] = sub i32 0, [[A]]
|
|
; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 0
|
|
; CHECK-NEXT: [[M:%.*]] = mul i32 [[SEL]], [[SUB]]
|
|
; CHECK-NEXT: ret i32 [[M]]
|
|
;
|
|
%s = sub i32 0, %p
|
|
%a = ashr exact i32 %s, 2
|
|
%cmp = icmp sgt i32 0, %a
|
|
%sub = sub nsw i32 0, %a
|
|
%sel = select i1 %cmp, i32 %sub, i32 0
|
|
%s2 = sub i32 0, %a
|
|
%m = mul i32 %sel, %s2
|
|
ret i32 %m
|
|
}
|
|
|
|
define float @maxnum(float %a, float %b) {
|
|
; CHECK-LABEL: @maxnum(
|
|
; CHECK-NEXT: [[X:%.*]] = call float @llvm.maxnum.f32(float [[A:%.*]], float [[B:%.*]])
|
|
; CHECK-NEXT: ret float 1.000000e+00
|
|
;
|
|
%x = call float @llvm.maxnum.f32(float %a, float %b)
|
|
%y = call float @llvm.maxnum.f32(float %b, float %a)
|
|
%r = fdiv nnan float %x, %y
|
|
ret float %r
|
|
}
|
|
|
|
define <2 x float> @minnum(<2 x float> %a, <2 x float> %b) {
|
|
; CHECK-LABEL: @minnum(
|
|
; CHECK-NEXT: [[X:%.*]] = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> [[A:%.*]], <2 x float> [[B:%.*]])
|
|
; CHECK-NEXT: ret <2 x float> <float 1.000000e+00, float 1.000000e+00>
|
|
;
|
|
%x = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> %a, <2 x float> %b)
|
|
%y = call fast <2 x float> @llvm.minnum.v2f32(<2 x float> %b, <2 x float> %a)
|
|
%r = fdiv nnan <2 x float> %x, %y
|
|
ret <2 x float> %r
|
|
}
|
|
|
|
define <2 x double> @maximum(<2 x double> %a, <2 x double> %b) {
|
|
; CHECK-LABEL: @maximum(
|
|
; CHECK-NEXT: [[X:%.*]] = call <2 x double> @llvm.maximum.v2f64(<2 x double> [[A:%.*]], <2 x double> [[B:%.*]])
|
|
; CHECK-NEXT: ret <2 x double> <double 1.000000e+00, double 1.000000e+00>
|
|
;
|
|
%x = call fast <2 x double> @llvm.maximum.v2f64(<2 x double> %a, <2 x double> %b)
|
|
%y = call <2 x double> @llvm.maximum.v2f64(<2 x double> %b, <2 x double> %a)
|
|
%r = fdiv nnan <2 x double> %x, %y
|
|
ret <2 x double> %r
|
|
}
|
|
|
|
define double @minimum(double %a, double %b) {
|
|
; CHECK-LABEL: @minimum(
|
|
; CHECK-NEXT: [[X:%.*]] = call double @llvm.minimum.f64(double [[A:%.*]], double [[B:%.*]])
|
|
; CHECK-NEXT: ret double 1.000000e+00
|
|
;
|
|
%x = call nsz double @llvm.minimum.f64(double %a, double %b)
|
|
%y = call ninf double @llvm.minimum.f64(double %b, double %a)
|
|
%r = fdiv nnan double %x, %y
|
|
ret double %r
|
|
}
|
|
define i16 @sadd_ov(i16 %a, i16 %b) {
|
|
; CHECK-LABEL: @sadd_ov(
|
|
; CHECK-NEXT: [[X:%.*]] = call { i16, i1 } @llvm.sadd.with.overflow.i16(i16 [[A:%.*]], i16 [[B:%.*]])
|
|
; CHECK-NEXT: [[X1:%.*]] = extractvalue { i16, i1 } [[X]], 0
|
|
; CHECK-NEXT: ret i16 [[X1]]
|
|
;
|
|
%x = call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %a, i16 %b)
|
|
%y = call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %b, i16 %a)
|
|
%x1 = extractvalue {i16, i1} %x, 0
|
|
%y1 = extractvalue {i16, i1} %y, 0
|
|
%o = or i16 %x1, %y1
|
|
ret i16 %o
|
|
}
|
|
|
|
define <5 x i65> @uadd_ov(<5 x i65> %a, <5 x i65> %b) {
|
|
; CHECK-LABEL: @uadd_ov(
|
|
; CHECK-NEXT: [[X:%.*]] = call { <5 x i65>, <5 x i1> } @llvm.uadd.with.overflow.v5i65(<5 x i65> [[A:%.*]], <5 x i65> [[B:%.*]])
|
|
; CHECK-NEXT: [[X1:%.*]] = extractvalue { <5 x i65>, <5 x i1> } [[X]], 0
|
|
; CHECK-NEXT: ret <5 x i65> [[X1]]
|
|
;
|
|
%x = call {<5 x i65>, <5 x i1>} @llvm.uadd.with.overflow.v5i65(<5 x i65> %a, <5 x i65> %b)
|
|
%y = call {<5 x i65>, <5 x i1>} @llvm.uadd.with.overflow.v5i65(<5 x i65> %b, <5 x i65> %a)
|
|
%x1 = extractvalue {<5 x i65>, <5 x i1>} %x, 0
|
|
%y1 = extractvalue {<5 x i65>, <5 x i1>} %y, 0
|
|
%o = or <5 x i65> %x1, %y1
|
|
ret <5 x i65> %o
|
|
}
|
|
|
|
define i37 @smul_ov(i37 %a, i37 %b) {
|
|
; CHECK-LABEL: @smul_ov(
|
|
; CHECK-NEXT: [[X:%.*]] = call { i37, i1 } @llvm.smul.with.overflow.i37(i37 [[A:%.*]], i37 [[B:%.*]])
|
|
; CHECK-NEXT: [[X1:%.*]] = extractvalue { i37, i1 } [[X]], 0
|
|
; CHECK-NEXT: ret i37 [[X1]]
|
|
;
|
|
%x = call {i37, i1} @llvm.smul.with.overflow.i37(i37 %a, i37 %b)
|
|
%y = call {i37, i1} @llvm.smul.with.overflow.i37(i37 %b, i37 %a)
|
|
%x1 = extractvalue {i37, i1} %x, 0
|
|
%y1 = extractvalue {i37, i1} %y, 0
|
|
%o = or i37 %x1, %y1
|
|
ret i37 %o
|
|
}
|
|
|
|
define <2 x i31> @umul_ov(<2 x i31> %a, <2 x i31> %b) {
|
|
; CHECK-LABEL: @umul_ov(
|
|
; CHECK-NEXT: [[X:%.*]] = call { <2 x i31>, <2 x i1> } @llvm.umul.with.overflow.v2i31(<2 x i31> [[A:%.*]], <2 x i31> [[B:%.*]])
|
|
; CHECK-NEXT: [[X1:%.*]] = extractvalue { <2 x i31>, <2 x i1> } [[X]], 0
|
|
; CHECK-NEXT: ret <2 x i31> [[X1]]
|
|
;
|
|
%x = call {<2 x i31>, <2 x i1>} @llvm.umul.with.overflow.v2i31(<2 x i31> %a, <2 x i31> %b)
|
|
%y = call {<2 x i31>, <2 x i1>} @llvm.umul.with.overflow.v2i31(<2 x i31> %b, <2 x i31> %a)
|
|
%x1 = extractvalue {<2 x i31>, <2 x i1>} %x, 0
|
|
%y1 = extractvalue {<2 x i31>, <2 x i1>} %y, 0
|
|
%o = or <2 x i31> %x1, %y1
|
|
ret <2 x i31> %o
|
|
}
|
|
|
|
define i64 @sadd_sat(i64 %a, i64 %b) {
|
|
; CHECK-LABEL: @sadd_sat(
|
|
; CHECK-NEXT: [[X:%.*]] = call i64 @llvm.sadd.sat.i64(i64 [[A:%.*]], i64 [[B:%.*]])
|
|
; CHECK-NEXT: ret i64 [[X]]
|
|
;
|
|
%x = call i64 @llvm.sadd.sat.i64(i64 %a, i64 %b)
|
|
%y = call i64 @llvm.sadd.sat.i64(i64 %b, i64 %a)
|
|
%o = or i64 %x, %y
|
|
ret i64 %o
|
|
}
|
|
|
|
define <2 x i64> @uadd_sat(<2 x i64> %a, <2 x i64> %b) {
|
|
; CHECK-LABEL: @uadd_sat(
|
|
; CHECK-NEXT: [[X:%.*]] = call <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64> [[A:%.*]], <2 x i64> [[B:%.*]])
|
|
; CHECK-NEXT: ret <2 x i64> [[X]]
|
|
;
|
|
%x = call <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64> %a, <2 x i64> %b)
|
|
%y = call <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64> %b, <2 x i64> %a)
|
|
%o = or <2 x i64> %x, %y
|
|
ret <2 x i64> %o
|
|
}
|
|
|
|
define <2 x i64> @smax(<2 x i64> %a, <2 x i64> %b) {
|
|
; CHECK-LABEL: @smax(
|
|
; CHECK-NEXT: [[X:%.*]] = call <2 x i64> @llvm.smax.v2i64(<2 x i64> [[A:%.*]], <2 x i64> [[B:%.*]])
|
|
; CHECK-NEXT: ret <2 x i64> [[X]]
|
|
;
|
|
%x = call <2 x i64> @llvm.smax.v2i64(<2 x i64> %a, <2 x i64> %b)
|
|
%y = call <2 x i64> @llvm.smax.v2i64(<2 x i64> %b, <2 x i64> %a)
|
|
%o = or <2 x i64> %x, %y
|
|
ret <2 x i64> %o
|
|
}
|
|
|
|
define i4 @smin(i4 %a, i4 %b) {
|
|
; CHECK-LABEL: @smin(
|
|
; CHECK-NEXT: [[X:%.*]] = call i4 @llvm.smin.i4(i4 [[A:%.*]], i4 [[B:%.*]])
|
|
; CHECK-NEXT: ret i4 [[X]]
|
|
;
|
|
%x = call i4 @llvm.smin.i4(i4 %a, i4 %b)
|
|
%y = call i4 @llvm.smin.i4(i4 %b, i4 %a)
|
|
%o = or i4 %x, %y
|
|
ret i4 %o
|
|
}
|
|
|
|
define i67 @umax(i67 %a, i67 %b) {
|
|
; CHECK-LABEL: @umax(
|
|
; CHECK-NEXT: [[X:%.*]] = call i67 @llvm.umax.i67(i67 [[A:%.*]], i67 [[B:%.*]])
|
|
; CHECK-NEXT: ret i67 [[X]]
|
|
;
|
|
%x = call i67 @llvm.umax.i67(i67 %a, i67 %b)
|
|
%y = call i67 @llvm.umax.i67(i67 %b, i67 %a)
|
|
%o = or i67 %x, %y
|
|
ret i67 %o
|
|
}
|
|
|
|
define <3 x i17> @umin(<3 x i17> %a, <3 x i17> %b) {
|
|
; CHECK-LABEL: @umin(
|
|
; CHECK-NEXT: [[X:%.*]] = call <3 x i17> @llvm.umin.v3i17(<3 x i17> [[A:%.*]], <3 x i17> [[B:%.*]])
|
|
; CHECK-NEXT: ret <3 x i17> [[X]]
|
|
;
|
|
%x = call <3 x i17> @llvm.umin.v3i17(<3 x i17> %a, <3 x i17> %b)
|
|
%y = call <3 x i17> @llvm.umin.v3i17(<3 x i17> %b, <3 x i17> %a)
|
|
%o = or <3 x i17> %x, %y
|
|
ret <3 x i17> %o
|
|
}
|
|
|
|
; Negative test - mismatched intrinsics
|
|
|
|
define i4 @smin_umin(i4 %a, i4 %b) {
|
|
; CHECK-LABEL: @smin_umin(
|
|
; CHECK-NEXT: [[X:%.*]] = call i4 @llvm.smin.i4(i4 [[A:%.*]], i4 [[B:%.*]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call i4 @llvm.umin.i4(i4 [[B]], i4 [[A]])
|
|
; CHECK-NEXT: [[O:%.*]] = or i4 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i4 [[O]]
|
|
;
|
|
%x = call i4 @llvm.smin.i4(i4 %a, i4 %b)
|
|
%y = call i4 @llvm.umin.i4(i4 %b, i4 %a)
|
|
%o = or i4 %x, %y
|
|
ret i4 %o
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define i16 @smul_fix(i16 %a, i16 %b) {
|
|
; CHECK-LABEL: @smul_fix(
|
|
; CHECK-NEXT: [[X:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[A:%.*]], i16 [[B:%.*]], i32 3)
|
|
; CHECK-NEXT: [[Y:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[B]], i16 [[A]], i32 3)
|
|
; CHECK-NEXT: [[O:%.*]] = or i16 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i16 [[O]]
|
|
;
|
|
%x = call i16 @llvm.smul.fix.i16(i16 %a, i16 %b, i32 3)
|
|
%y = call i16 @llvm.smul.fix.i16(i16 %b, i16 %a, i32 3)
|
|
%o = or i16 %x, %y
|
|
ret i16 %o
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define i16 @umul_fix(i16 %a, i16 %b, i32 %s) {
|
|
; CHECK-LABEL: @umul_fix(
|
|
; CHECK-NEXT: [[X:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[A:%.*]], i16 [[B:%.*]], i32 1)
|
|
; CHECK-NEXT: [[Y:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[B]], i16 [[A]], i32 1)
|
|
; CHECK-NEXT: [[O:%.*]] = or i16 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i16 [[O]]
|
|
;
|
|
%x = call i16 @llvm.umul.fix.i16(i16 %a, i16 %b, i32 1)
|
|
%y = call i16 @llvm.umul.fix.i16(i16 %b, i16 %a, i32 1)
|
|
%o = or i16 %x, %y
|
|
ret i16 %o
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define <3 x i16> @smul_fix_sat(<3 x i16> %a, <3 x i16> %b) {
|
|
; CHECK-LABEL: @smul_fix_sat(
|
|
; CHECK-NEXT: [[X:%.*]] = call <3 x i16> @llvm.smul.fix.sat.v3i16(<3 x i16> [[A:%.*]], <3 x i16> [[B:%.*]], i32 2)
|
|
; CHECK-NEXT: [[Y:%.*]] = call <3 x i16> @llvm.smul.fix.sat.v3i16(<3 x i16> [[B]], <3 x i16> [[A]], i32 2)
|
|
; CHECK-NEXT: [[O:%.*]] = or <3 x i16> [[X]], [[Y]]
|
|
; CHECK-NEXT: ret <3 x i16> [[O]]
|
|
;
|
|
%x = call <3 x i16> @llvm.smul.fix.sat.v3i16(<3 x i16> %a, <3 x i16> %b, i32 2)
|
|
%y = call <3 x i16> @llvm.smul.fix.sat.v3i16(<3 x i16> %b, <3 x i16> %a, i32 2)
|
|
%o = or <3 x i16> %x, %y
|
|
ret <3 x i16> %o
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define <3 x i16> @umul_fix_sat(<3 x i16> %a, <3 x i16> %b) {
|
|
; CHECK-LABEL: @umul_fix_sat(
|
|
; CHECK-NEXT: [[X:%.*]] = call <3 x i16> @llvm.umul.fix.sat.v3i16(<3 x i16> [[A:%.*]], <3 x i16> [[B:%.*]], i32 3)
|
|
; CHECK-NEXT: [[Y:%.*]] = call <3 x i16> @llvm.umul.fix.sat.v3i16(<3 x i16> [[B]], <3 x i16> [[A]], i32 3)
|
|
; CHECK-NEXT: [[O:%.*]] = or <3 x i16> [[X]], [[Y]]
|
|
; CHECK-NEXT: ret <3 x i16> [[O]]
|
|
;
|
|
%x = call <3 x i16> @llvm.umul.fix.sat.v3i16(<3 x i16> %a, <3 x i16> %b, i32 3)
|
|
%y = call <3 x i16> @llvm.umul.fix.sat.v3i16(<3 x i16> %b, <3 x i16> %a, i32 3)
|
|
%o = or <3 x i16> %x, %y
|
|
ret <3 x i16> %o
|
|
}
|
|
|
|
define i16 @umul_smul_fix(i16 %a, i16 %b, i32 %s) {
|
|
; CHECK-LABEL: @umul_smul_fix(
|
|
; CHECK-NEXT: [[X:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[A:%.*]], i16 [[B:%.*]], i32 1)
|
|
; CHECK-NEXT: [[Y:%.*]] = call i16 @llvm.smul.fix.i16(i16 [[B]], i16 [[A]], i32 1)
|
|
; CHECK-NEXT: [[O:%.*]] = or i16 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i16 [[O]]
|
|
;
|
|
%x = call i16 @llvm.umul.fix.i16(i16 %a, i16 %b, i32 1)
|
|
%y = call i16 @llvm.smul.fix.i16(i16 %b, i16 %a, i32 1)
|
|
%o = or i16 %x, %y
|
|
ret i16 %o
|
|
}
|
|
|
|
define i16 @umul_fix_scale(i16 %a, i16 %b, i32 %s) {
|
|
; CHECK-LABEL: @umul_fix_scale(
|
|
; CHECK-NEXT: [[X:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[A:%.*]], i16 [[B:%.*]], i32 1)
|
|
; CHECK-NEXT: [[Y:%.*]] = call i16 @llvm.umul.fix.i16(i16 [[B]], i16 [[A]], i32 2)
|
|
; CHECK-NEXT: [[O:%.*]] = or i16 [[X]], [[Y]]
|
|
; CHECK-NEXT: ret i16 [[O]]
|
|
;
|
|
%x = call i16 @llvm.umul.fix.i16(i16 %a, i16 %b, i32 1)
|
|
%y = call i16 @llvm.umul.fix.i16(i16 %b, i16 %a, i32 2)
|
|
%o = or i16 %x, %y
|
|
ret i16 %o
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define float @fma(float %a, float %b, float %c) {
|
|
; CHECK-LABEL: @fma(
|
|
; CHECK-NEXT: [[X:%.*]] = call float @llvm.fma.f32(float [[A:%.*]], float [[B:%.*]], float [[C:%.*]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call float @llvm.fma.f32(float [[B]], float [[A]], float [[C]])
|
|
; CHECK-NEXT: [[R:%.*]] = fdiv nnan float [[X]], [[Y]]
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%x = call float @llvm.fma.f32(float %a, float %b, float %c)
|
|
%y = call float @llvm.fma.f32(float %b, float %a, float %c)
|
|
%r = fdiv nnan float %x, %y
|
|
ret float %r
|
|
}
|
|
|
|
define float @fma_different_add_ops(float %a, float %b, float %c, float %d) {
|
|
; CHECK-LABEL: @fma_different_add_ops(
|
|
; CHECK-NEXT: [[X:%.*]] = call float @llvm.fma.f32(float [[A:%.*]], float [[B:%.*]], float [[C:%.*]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call float @llvm.fma.f32(float [[B]], float [[A]], float [[D:%.*]])
|
|
; CHECK-NEXT: [[R:%.*]] = fdiv nnan float [[X]], [[Y]]
|
|
; CHECK-NEXT: ret float [[R]]
|
|
;
|
|
%x = call float @llvm.fma.f32(float %a, float %b, float %c)
|
|
%y = call float @llvm.fma.f32(float %b, float %a, float %d)
|
|
%r = fdiv nnan float %x, %y
|
|
ret float %r
|
|
}
|
|
|
|
; TODO: handle >2 args
|
|
|
|
define <2 x double> @fmuladd(<2 x double> %a, <2 x double> %b, <2 x double> %c) {
|
|
; CHECK-LABEL: @fmuladd(
|
|
; CHECK-NEXT: [[X:%.*]] = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> [[A:%.*]], <2 x double> [[B:%.*]], <2 x double> [[C:%.*]])
|
|
; CHECK-NEXT: [[Y:%.*]] = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> [[B]], <2 x double> [[A]], <2 x double> [[C]])
|
|
; CHECK-NEXT: [[R:%.*]] = fdiv nnan <2 x double> [[X]], [[Y]]
|
|
; CHECK-NEXT: ret <2 x double> [[R]]
|
|
;
|
|
%x = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> %a, <2 x double> %b, <2 x double> %c)
|
|
%y = call <2 x double> @llvm.fmuladd.v2f64(<2 x double> %b, <2 x double> %a, <2 x double> %c)
|
|
%r = fdiv nnan <2 x double> %x, %y
|
|
ret <2 x double> %r
|
|
}
|
|
|
|
declare float @llvm.maxnum.f32(float, float)
|
|
declare <2 x float> @llvm.minnum.v2f32(<2 x float>, <2 x float>)
|
|
declare <2 x double> @llvm.maximum.v2f64(<2 x double>, <2 x double>)
|
|
declare double @llvm.minimum.f64(double, double)
|
|
|
|
declare {i16, i1} @llvm.sadd.with.overflow.i16(i16, i16)
|
|
declare {<5 x i65>, <5 x i1>} @llvm.uadd.with.overflow.v5i65(<5 x i65>, <5 x i65>)
|
|
declare {i37, i1} @llvm.smul.with.overflow.i37(i37, i37)
|
|
declare {<2 x i31>, <2 x i1>} @llvm.umul.with.overflow.v2i31(<2 x i31>, <2 x i31>)
|
|
declare i64 @llvm.sadd.sat.i64(i64, i64)
|
|
declare <2 x i64> @llvm.uadd.sat.v2i64(<2 x i64>, <2 x i64>)
|
|
|
|
declare <2 x i64> @llvm.smax.v2i64(<2 x i64>, <2 x i64>)
|
|
declare i4 @llvm.smin.i4(i4, i4)
|
|
declare i4 @llvm.umin.i4(i4, i4)
|
|
declare i67 @llvm.umax.i67(i67, i67)
|
|
declare <3 x i17> @llvm.umin.v3i17(<3 x i17>, <3 x i17>)
|
|
|
|
declare i16 @llvm.smul.fix.i16(i16, i16, i32)
|
|
declare i16 @llvm.umul.fix.i16(i16, i16, i32)
|
|
declare <3 x i16> @llvm.smul.fix.sat.v3i16(<3 x i16>, <3 x i16>, i32)
|
|
declare <3 x i16> @llvm.umul.fix.sat.v3i16(<3 x i16>, <3 x i16>, i32)
|
|
|
|
declare float @llvm.fma.f32(float, float, float)
|
|
declare <2 x double> @llvm.fmuladd.v2f64(<2 x double>, <2 x double>, <2 x double>)
|