-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[InstCombine] Fold icmp with clamp into unsigned bound check #161303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
cffbd01
[InstCombine] Add baseline tests for icmp with clamp operation
brandonxin eab6eba
[InstCombine] Fold icmp with clamp to unsigned bound check
brandonxin 68c86f4
[InstCombine] Remove redundant checks
brandonxin 4eefa5e
[InstCombine] Improve header comments
brandonxin 9cde0b2
[InstCombine] Check if Min is actually a Min.
brandonxin f306854
[InstCombine] Avoid unnecessary addition when offset is zero
brandonxin af64a82
[InstComine] Use getNonEmpty to handle edge case
brandonxin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,295 @@ | ||
| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | ||
| ; RUN: opt < %s -passes=instcombine -S | FileCheck %s | ||
|
|
||
| declare void @use(i32) | ||
|
|
||
| define i1 @test_i32_eq(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i32_eq( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], 95 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 256 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_i32_ne(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i32_ne( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -161 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -256 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp ne i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_i32_eq_no_add(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i32_eq_no_add( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[X]], 161 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 0) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_i32_ne_no_add(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i32_ne_no_add( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 160 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 0) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp ne i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_unsigned_eq(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_unsigned_eq( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -10 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 91 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10) | ||
| %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 100) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_unsigned_ne(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_unsigned_ne( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], -101 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], -91 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10) | ||
| %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 100) | ||
| %cmp = icmp ne i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
|
|
||
| ; Different bit widths | ||
| define i1 @test_i8_eq(i8 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i8_eq( | ||
| ; CHECK-SAME: i8 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X]], 50 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[TMP1]], 101 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i8 @llvm.smax.i8(i8 %x, i8 -50) | ||
| %v2 = tail call i8 @llvm.smin.i8(i8 %v1, i8 50) | ||
| %cmp = icmp eq i8 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_i16_eq(i16 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i16_eq( | ||
| ; CHECK-SAME: i16 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i16 [[X]], 1000 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i16 [[TMP1]], 2001 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i16 @llvm.smax.i16(i16 %x, i16 -1000) | ||
| %v2 = tail call i16 @llvm.smin.i16(i16 %v1, i16 1000) | ||
| %cmp = icmp eq i16 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| define i1 @test_i64_eq(i64 %x) { | ||
| ; CHECK-LABEL: define i1 @test_i64_eq( | ||
| ; CHECK-SAME: i64 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i64 [[X]], 1 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[TMP1]], -1 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i64 @llvm.smax.i64(i64 %x, i64 -1) | ||
| %v2 = tail call i64 @llvm.smin.i64(i64 %v1, i64 9223372036854775806) | ||
| %cmp = icmp eq i64 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Negative tests - wrong predicate | ||
| define i1 @test_wrong_pred_slt(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_wrong_pred_slt( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], 160 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp slt i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
|
|
||
| ; Negative tests - not a clamp pattern | ||
| define i1 @test_not_clamp_pattern(i32 %x, i32 %y) { | ||
| ; CHECK-LABEL: define i1 @test_not_clamp_pattern( | ||
| ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { | ||
| ; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[Y]], i32 -95) | ||
| ; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]] | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %y, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Negative tests - Lo >= Hi | ||
| define i1 @test_invalid_range(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_invalid_range( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X]], 50 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 100) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 50) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Negative tests - Lo is minimum signed value | ||
| define i1 @test_lo_min_signed(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_lo_min_signed( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X]], 161 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -2147483648) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Negative tests - Hi is maximum signed value | ||
| define i1 @test_hi_max_signed(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_hi_max_signed( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], -96 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 2147483647) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Negative tests - Hi is maximum unsigned value | ||
| define i1 @test_hi_max_unsigned(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_hi_max_unsigned( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X]], 9 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.umax.i32(i32 %x, i32 10) | ||
| %v2 = tail call i32 @llvm.umin.i32(i32 %v1, i32 4294967295) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Multi-use tests - multiple uses of max | ||
| define i1 @test_multi_use_max(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_multi_use_max( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[X]], i32 -95) | ||
| ; CHECK-NEXT: call void @use(i32 [[V1]]) | ||
| ; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]] | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| call void @use(i32 %v1) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Multi-use tests - multiple uses of min | ||
| define i1 @test_multi_use_min(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_multi_use_min( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[V1:%.*]] = tail call i32 @llvm.smax.i32(i32 [[X]], i32 -95) | ||
| ; CHECK-NEXT: [[V2:%.*]] = tail call i32 @llvm.smin.i32(i32 [[V1]], i32 160) | ||
| ; CHECK-NEXT: call void @use(i32 [[V2]]) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[V2]], [[X]] | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| call void @use(i32 %v2) | ||
| %cmp = icmp eq i32 %v2, %x | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
| ; Commuted tests | ||
| define i1 @test_commuted_eq(i32 %x) { | ||
| ; CHECK-LABEL: define i1 @test_commuted_eq( | ||
| ; CHECK-SAME: i32 [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X]], 95 | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[TMP1]], 256 | ||
| ; CHECK-NEXT: ret i1 [[CMP]] | ||
| ; | ||
| %v1 = tail call i32 @llvm.smax.i32(i32 %x, i32 -95) | ||
| %v2 = tail call i32 @llvm.smin.i32(i32 %v1, i32 160) | ||
| %cmp = icmp eq i32 %x, %v2 | ||
| ret i1 %cmp | ||
| } | ||
|
|
||
|
|
||
| ; Vector tests - splat constants | ||
| define <2 x i1> @test_vec_splat_eq(<2 x i32> %x) { | ||
| ; CHECK-LABEL: define <2 x i1> @test_vec_splat_eq( | ||
| ; CHECK-SAME: <2 x i32> [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i32> [[X]], splat (i32 50) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp ult <2 x i32> [[TMP1]], splat (i32 101) | ||
| ; CHECK-NEXT: ret <2 x i1> [[CMP]] | ||
| ; | ||
| %v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 -50>) | ||
| %v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 50>) | ||
| %cmp = icmp eq <2 x i32> %v2, %x | ||
| ret <2 x i1> %cmp | ||
| } | ||
|
|
||
| ; Vector tests - poison elements | ||
| define <2 x i1> @test_vec_poison_eq(<2 x i32> %x) { | ||
| ; CHECK-LABEL: define <2 x i1> @test_vec_poison_eq( | ||
| ; CHECK-SAME: <2 x i32> [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[V1:%.*]] = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[X]], <2 x i32> <i32 -50, i32 poison>) | ||
| ; CHECK-NEXT: [[V2:%.*]] = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[V1]], <2 x i32> <i32 50, i32 poison>) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[V2]], [[X]] | ||
| ; CHECK-NEXT: ret <2 x i1> [[CMP]] | ||
| ; | ||
| %v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 poison>) | ||
| %v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 poison>) | ||
| %cmp = icmp eq <2 x i32> %v2, %x | ||
| ret <2 x i1> %cmp | ||
| } | ||
|
|
||
| ; Vector tests - non-splat | ||
| define <2 x i1> @test_vec_non_splat_eq(<2 x i32> %x) { | ||
| ; CHECK-LABEL: define <2 x i1> @test_vec_non_splat_eq( | ||
| ; CHECK-SAME: <2 x i32> [[X:%.*]]) { | ||
| ; CHECK-NEXT: [[V1:%.*]] = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> [[X]], <2 x i32> <i32 -50, i32 -30>) | ||
| ; CHECK-NEXT: [[V2:%.*]] = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[V1]], <2 x i32> <i32 50, i32 70>) | ||
| ; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[V2]], [[X]] | ||
| ; CHECK-NEXT: ret <2 x i1> [[CMP]] | ||
| ; | ||
| %v1 = tail call <2 x i32> @llvm.smax.v2i32(<2 x i32> %x, <2 x i32> <i32 -50, i32 -30>) | ||
| %v2 = tail call <2 x i32> @llvm.smin.v2i32(<2 x i32> %v1, <2 x i32> <i32 50, i32 70>) | ||
| %cmp = icmp eq <2 x i32> %v2, %x | ||
| ret <2 x i1> %cmp | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we also check that this is actually a min and not a max intrinsic?
I guess the max one will get folded away, but better not rely on it due to worklist order issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes you are right. Without those preliminary transforms, there will be a problem here. I added
isMinandisMaxhelper methods toMinMaxIntrinsicand then added a check here. Thanks for pointing that out.