Files
clang-p2996/llvm/test/Transforms/SCCP/with.overflow.ll
Nikita Popov f101196d69 [SCCP] Add support for with.overflow intrinsics
This adds SCCP support for extractvalues of with.overflow.
We compute both the range of the result value and determine when
the overflow value is always false.

Differential Revision: https://reviews.llvm.org/D137713
2022-12-05 12:00:22 +01:00

287 lines
14 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -sccp < %s | FileCheck %s
declare { i8, i1 } @llvm.uadd.with.overflow.i8(i8, i8)
declare { i8, i1 } @llvm.usub.with.overflow.i8(i8, i8)
declare { i8, i1 } @llvm.umul.with.overflow.i8(i8, i8)
declare { i8, i1 } @llvm.sadd.with.overflow.i8(i8, i8)
declare { i8, i1 } @llvm.ssub.with.overflow.i8(i8, i8)
declare { i8, i1 } @llvm.smul.with.overflow.i8(i8, i8)
declare { <2 x i8>, <2 x i1> } @llvm.uadd.with.overflow.v2i8(<2 x i8>, <2 x i8>)
declare void @use.i1(i1)
define void @unsigned_overflow(ptr %p) {
; CHECK-LABEL: @unsigned_overflow(
; CHECK-NEXT: [[V0_100:%.*]] = load i8, ptr [[P:%.*]], align 1, !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[V0_155:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG1:![0-9]+]]
; CHECK-NEXT: [[V0_156:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG2:![0-9]+]]
; CHECK-NEXT: [[V100_255:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG3:![0-9]+]]
; CHECK-NEXT: [[V99_255:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG4:![0-9]+]]
; CHECK-NEXT: [[V1_2:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG5:![0-9]+]]
; CHECK-NEXT: [[V1_3:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG6:![0-9]+]]
; CHECK-NEXT: [[WO1:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[V0_100]], i8 [[V0_155]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO2:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[V0_100]], i8 [[V0_156]])
; CHECK-NEXT: [[OV2:%.*]] = extractvalue { i8, i1 } [[WO2]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV2]])
; CHECK-NEXT: [[WO3:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[V100_255]], i8 [[V0_100]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO4:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[V99_255]], i8 [[V0_100]])
; CHECK-NEXT: [[OV4:%.*]] = extractvalue { i8, i1 } [[WO4]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV4]])
; CHECK-NEXT: [[WO5:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[V0_100]], i8 [[V1_2]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO6:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[V0_100]], i8 [[V1_3]])
; CHECK-NEXT: [[OV6:%.*]] = extractvalue { i8, i1 } [[WO6]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV6]])
; CHECK-NEXT: ret void
;
%v0_100 = load i8, ptr %p, !range !{i8 0, i8 101}
%v0_155 = load i8, ptr %p, !range !{i8 0, i8 156}
%v0_156 = load i8, ptr %p, !range !{i8 0, i8 157}
%v100_255 = load i8, ptr %p, !range !{i8 100, i8 0}
%v99_255 = load i8, ptr %p, !range !{i8 99, i8 0}
%v1_2 = load i8, ptr %p, !range !{i8 1, i8 3}
%v1_3 = load i8, ptr %p, !range !{i8 1, i8 4}
%wo1 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %v0_100, i8 %v0_155)
%ov1 = extractvalue { i8, i1 } %wo1, 1
call void @use.i1(i1 %ov1)
%wo2 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %v0_100, i8 %v0_156)
%ov2 = extractvalue { i8, i1 } %wo2, 1
call void @use.i1(i1 %ov2)
%wo3 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %v100_255, i8 %v0_100)
%ov3 = extractvalue { i8, i1 } %wo3, 1
call void @use.i1(i1 %ov3)
%wo4 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %v99_255, i8 %v0_100)
%ov4 = extractvalue { i8, i1 } %wo4, 1
call void @use.i1(i1 %ov4)
%wo5 = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 %v0_100, i8 %v1_2)
%ov5 = extractvalue { i8, i1 } %wo5, 1
call void @use.i1(i1 %ov5)
%wo6 = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 %v0_100, i8 %v1_3)
%ov6 = extractvalue { i8, i1 } %wo6, 1
call void @use.i1(i1 %ov6)
ret void
}
define void @signed_overflow(ptr %p) {
; CHECK-LABEL: @signed_overflow(
; CHECK-NEXT: [[V0_100:%.*]] = load i8, ptr [[P:%.*]], align 1, !range [[RNG0]]
; CHECK-NEXT: [[V0_27:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG7:![0-9]+]]
; CHECK-NEXT: [[V0_28:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG8:![0-9]+]]
; CHECK-NEXT: [[VM27_0:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG9:![0-9]+]]
; CHECK-NEXT: [[VM28_0:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG10:![0-9]+]]
; CHECK-NEXT: [[V1_4:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG11:![0-9]+]]
; CHECK-NEXT: [[V1_5:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG12:![0-9]+]]
; CHECK-NEXT: [[WO1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[V0_100]], i8 [[V0_27]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO2:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[V0_100]], i8 [[V0_28]])
; CHECK-NEXT: [[OV2:%.*]] = extractvalue { i8, i1 } [[WO2]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV2]])
; CHECK-NEXT: [[WO3:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[V0_100]], i8 [[VM27_0]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO4:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[V0_100]], i8 [[VM28_0]])
; CHECK-NEXT: [[OV4:%.*]] = extractvalue { i8, i1 } [[WO4]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV4]])
; CHECK-NEXT: [[WO5:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[V0_27]], i8 [[V1_4]])
; CHECK-NEXT: call void @use.i1(i1 false)
; CHECK-NEXT: [[WO6:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[V0_27]], i8 [[V1_5]])
; CHECK-NEXT: [[OV6:%.*]] = extractvalue { i8, i1 } [[WO6]], 1
; CHECK-NEXT: call void @use.i1(i1 [[OV6]])
; CHECK-NEXT: ret void
;
%v0_100 = load i8, ptr %p, !range !{i8 0, i8 101}
%v0_27 = load i8, ptr %p, !range !{i8 0, i8 28}
%v0_28 = load i8, ptr %p, !range !{i8 0, i8 29}
%vm27_0 = load i8, ptr %p, !range !{i8 -27, i8 0}
%vm28_0 = load i8, ptr %p, !range !{i8 -28, i8 0}
%v1_4 = load i8, ptr %p, !range !{i8 1, i8 5}
%v1_5 = load i8, ptr %p, !range !{i8 1, i8 6}
%wo1 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %v0_100, i8 %v0_27)
%ov1 = extractvalue { i8, i1 } %wo1, 1
call void @use.i1(i1 %ov1)
%wo2 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %v0_100, i8 %v0_28)
%ov2 = extractvalue { i8, i1 } %wo2, 1
call void @use.i1(i1 %ov2)
%wo3 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %v0_100, i8 %vm27_0)
%ov3 = extractvalue { i8, i1 } %wo3, 1
call void @use.i1(i1 %ov3)
%wo4 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %v0_100, i8 %vm28_0)
%ov4 = extractvalue { i8, i1 } %wo4, 1
call void @use.i1(i1 %ov4)
%wo5 = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %v0_27, i8 %v1_4)
%ov5 = extractvalue { i8, i1 } %wo5, 1
call void @use.i1(i1 %ov5)
%wo6 = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %v0_27, i8 %v1_5)
%ov6 = extractvalue { i8, i1 } %wo6, 1
call void @use.i1(i1 %ov6)
ret void
}
define void @unsigned_result(ptr %p) {
; CHECK-LABEL: @unsigned_result(
; CHECK-NEXT: [[V0_20:%.*]] = load i8, ptr [[P:%.*]], align 1, !range [[RNG13:![0-9]+]]
; CHECK-NEXT: [[V20_40:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG14:![0-9]+]]
; CHECK-NEXT: [[V0_10:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG15:![0-9]+]]
; CHECK-NEXT: [[V2_3:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG16:![0-9]+]]
; CHECK-NEXT: [[WO1:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[V0_20]], i8 [[V20_40]])
; CHECK-NEXT: [[RES1:%.*]] = extractvalue { i8, i1 } [[WO1]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP1_3:%.*]] = icmp ugt i8 [[RES1]], 20
; CHECK-NEXT: call void @use.i1(i1 [[CMP1_3]])
; CHECK-NEXT: [[CMP1_4:%.*]] = icmp ult i8 [[RES1]], 60
; CHECK-NEXT: call void @use.i1(i1 [[CMP1_4]])
; CHECK-NEXT: [[WO2:%.*]] = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 [[V0_10]], i8 [[V20_40]])
; CHECK-NEXT: [[RES2:%.*]] = extractvalue { i8, i1 } [[WO2]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP2_3:%.*]] = icmp ugt i8 [[RES2]], -40
; CHECK-NEXT: call void @use.i1(i1 [[CMP2_3]])
; CHECK-NEXT: [[CMP2_4:%.*]] = icmp ult i8 [[RES2]], -10
; CHECK-NEXT: call void @use.i1(i1 [[CMP2_4]])
; CHECK-NEXT: [[WO3:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 [[V20_40]], i8 [[V2_3]])
; CHECK-NEXT: [[RES3:%.*]] = extractvalue { i8, i1 } [[WO3]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP3_3:%.*]] = icmp ugt i8 [[RES3]], 40
; CHECK-NEXT: call void @use.i1(i1 [[CMP3_3]])
; CHECK-NEXT: [[CMP3_4:%.*]] = icmp ult i8 [[RES3]], 120
; CHECK-NEXT: call void @use.i1(i1 [[CMP3_4]])
; CHECK-NEXT: ret void
;
%v0_20 = load i8, ptr %p, !range !{i8 0, i8 21}
%v20_40 = load i8, ptr %p, !range !{i8 20, i8 41}
%v0_10 = load i8, ptr %p, !range !{i8 0, i8 11}
%v2_3 = load i8, ptr %p, !range !{i8 2, i8 4}
%wo1 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %v0_20, i8 %v20_40)
%res1 = extractvalue { i8, i1 } %wo1, 0
%cmp1.1 = icmp uge i8 %res1, 20
call void @use.i1(i1 %cmp1.1)
%cmp1.2 = icmp ule i8 %res1, 60
call void @use.i1(i1 %cmp1.2)
%cmp1.3 = icmp ugt i8 %res1, 20
call void @use.i1(i1 %cmp1.3)
%cmp1.4 = icmp ult i8 %res1, 60
call void @use.i1(i1 %cmp1.4)
; This case actually does overflow, but we can still determine the range.
%wo2 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %v0_10, i8 %v20_40)
%res2 = extractvalue { i8, i1 } %wo2, 0
%cmp2.1 = icmp uge i8 %res2, -40
call void @use.i1(i1 %cmp2.1)
%cmp2.2 = icmp ule i8 %res2, -10
call void @use.i1(i1 %cmp2.2)
%cmp2.3 = icmp ugt i8 %res2, -40
call void @use.i1(i1 %cmp2.3)
%cmp2.4 = icmp ult i8 %res2, -10
call void @use.i1(i1 %cmp2.4)
%wo3 = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 %v20_40, i8 %v2_3)
%res3 = extractvalue { i8, i1 } %wo3, 0
%cmp3.1 = icmp uge i8 %res3, 40
call void @use.i1(i1 %cmp3.1)
%cmp3.2 = icmp ule i8 %res3, 120
call void @use.i1(i1 %cmp3.2)
%cmp3.3 = icmp ugt i8 %res3, 40
call void @use.i1(i1 %cmp3.3)
%cmp3.4 = icmp ult i8 %res3, 120
call void @use.i1(i1 %cmp3.4)
ret void
}
define void @signed_result(ptr %p) {
; CHECK-LABEL: @signed_result(
; CHECK-NEXT: [[V0_20:%.*]] = load i8, ptr [[P:%.*]], align 1, !range [[RNG13]]
; CHECK-NEXT: [[V20_40:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG14]]
; CHECK-NEXT: [[V0_10:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG15]]
; CHECK-NEXT: [[V2_3:%.*]] = load i8, ptr [[P]], align 1, !range [[RNG16]]
; CHECK-NEXT: [[WO1:%.*]] = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 [[V0_20]], i8 [[V20_40]])
; CHECK-NEXT: [[RES1:%.*]] = extractvalue { i8, i1 } [[WO1]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP1_3:%.*]] = icmp ugt i8 [[RES1]], 20
; CHECK-NEXT: call void @use.i1(i1 [[CMP1_3]])
; CHECK-NEXT: [[CMP1_4:%.*]] = icmp ult i8 [[RES1]], 60
; CHECK-NEXT: call void @use.i1(i1 [[CMP1_4]])
; CHECK-NEXT: [[WO2:%.*]] = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 [[V0_10]], i8 [[V20_40]])
; CHECK-NEXT: [[RES2:%.*]] = extractvalue { i8, i1 } [[WO2]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP2_3:%.*]] = icmp ugt i8 [[RES2]], -40
; CHECK-NEXT: call void @use.i1(i1 [[CMP2_3]])
; CHECK-NEXT: [[CMP2_4:%.*]] = icmp ult i8 [[RES2]], -10
; CHECK-NEXT: call void @use.i1(i1 [[CMP2_4]])
; CHECK-NEXT: [[WO3:%.*]] = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 [[V20_40]], i8 [[V2_3]])
; CHECK-NEXT: [[RES3:%.*]] = extractvalue { i8, i1 } [[WO3]], 0
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: call void @use.i1(i1 true)
; CHECK-NEXT: [[CMP3_3:%.*]] = icmp ugt i8 [[RES3]], 40
; CHECK-NEXT: call void @use.i1(i1 [[CMP3_3]])
; CHECK-NEXT: [[CMP3_4:%.*]] = icmp ult i8 [[RES3]], 120
; CHECK-NEXT: call void @use.i1(i1 [[CMP3_4]])
; CHECK-NEXT: ret void
;
%v0_20 = load i8, ptr %p, !range !{i8 0, i8 21}
%v20_40 = load i8, ptr %p, !range !{i8 20, i8 41}
%v0_10 = load i8, ptr %p, !range !{i8 0, i8 11}
%v2_3 = load i8, ptr %p, !range !{i8 2, i8 4}
%wo1 = call { i8, i1 } @llvm.sadd.with.overflow.i8(i8 %v0_20, i8 %v20_40)
%res1 = extractvalue { i8, i1 } %wo1, 0
%cmp1.1 = icmp uge i8 %res1, 20
call void @use.i1(i1 %cmp1.1)
%cmp1.2 = icmp ule i8 %res1, 60
call void @use.i1(i1 %cmp1.2)
%cmp1.3 = icmp ugt i8 %res1, 20
call void @use.i1(i1 %cmp1.3)
%cmp1.4 = icmp ult i8 %res1, 60
call void @use.i1(i1 %cmp1.4)
%wo2 = call { i8, i1 } @llvm.ssub.with.overflow.i8(i8 %v0_10, i8 %v20_40)
%res2 = extractvalue { i8, i1 } %wo2, 0
%cmp2.1 = icmp uge i8 %res2, -40
call void @use.i1(i1 %cmp2.1)
%cmp2.2 = icmp ule i8 %res2, -10
call void @use.i1(i1 %cmp2.2)
%cmp2.3 = icmp ugt i8 %res2, -40
call void @use.i1(i1 %cmp2.3)
%cmp2.4 = icmp ult i8 %res2, -10
call void @use.i1(i1 %cmp2.4)
%wo3 = call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %v20_40, i8 %v2_3)
%res3 = extractvalue { i8, i1 } %wo3, 0
%cmp3.1 = icmp uge i8 %res3, 40
call void @use.i1(i1 %cmp3.1)
%cmp3.2 = icmp ule i8 %res3, 120
call void @use.i1(i1 %cmp3.2)
%cmp3.3 = icmp ugt i8 %res3, 40
call void @use.i1(i1 %cmp3.3)
%cmp3.4 = icmp ult i8 %res3, 120
call void @use.i1(i1 %cmp3.4)
ret void
}
; SCCP doesn't really support vector ranges yet, just make sure we don't crash.
define <2 x i1> @vec(<2 x i8> %v1, <2 x i8> %v2) {
; CHECK-LABEL: @vec(
; CHECK-NEXT: [[WO:%.*]] = call { <2 x i8>, <2 x i1> } @llvm.uadd.with.overflow.v2i8(<2 x i8> [[V1:%.*]], <2 x i8> [[V2:%.*]])
; CHECK-NEXT: [[OV:%.*]] = extractvalue { <2 x i8>, <2 x i1> } [[WO]], 1
; CHECK-NEXT: ret <2 x i1> [[OV]]
;
%wo = call { <2 x i8>, <2 x i1> } @llvm.uadd.with.overflow.v2i8(<2 x i8> %v1, <2 x i8> %v2)
%ov = extractvalue { <2 x i8>, <2 x i1> } %wo, 1
ret <2 x i1> %ov
}