| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -S -dxil-intrinsic-expansion -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s | |
| ; Verify the {u,s}mul.with.overflow intrinsics are expanded away, since DXIL has | |
| ; no llvm.*.with.overflow op. The overflow check is computed without a wider | |
| ; integer type so that no 64-bit ops are introduced. | |
| define { i32, i1 } @umul_i32(i32 %a, i32 %b) { | |
| ; CHECK-LABEL: define { i32, i1 } @umul_i32( | |
| ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call { i32, i32 } @llvm.dx.umul.i32(i32 [[A]], i32 [[B]]) | |
| ; CHECK-NEXT: [[TMP17:%.*]] = extractvalue { i32, i32 } [[TMP2]], 0 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { i32, i32 } [[TMP2]], 1 | |
| ; CHECK-NEXT: [[TMP18:%.*]] = icmp ne i32 [[TMP17]], 0 | |
| ; CHECK-NEXT: [[TMP19:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP20:%.*]] = insertvalue { i32, i1 } [[TMP19]], i1 [[TMP18]], 1 | |
| ; CHECK-NEXT: ret { i32, i1 } [[TMP20]] | |
| ; | |
| %r = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %a, i32 %b) | |
| ret { i32, i1 } %r | |
| } | |
| define { <4 x i32>, <4 x i1> } @umul_v4i32(<4 x i32> %a, <4 x i32> %b) { | |
| ; CHECK-LABEL: define { <4 x i32>, <4 x i1> } @umul_v4i32( | |
| ; CHECK-SAME: <4 x i32> [[A:%.*]], <4 x i32> [[B:%.*]]) { | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call { <4 x i32>, <4 x i32> } @llvm.dx.umul.v4i32(<4 x i32> [[A]], <4 x i32> [[B]]) | |
| ; CHECK-NEXT: [[TMP17:%.*]] = extractvalue { <4 x i32>, <4 x i32> } [[TMP2]], 0 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { <4 x i32>, <4 x i32> } [[TMP2]], 1 | |
| ; CHECK-NEXT: [[TMP18:%.*]] = icmp ne <4 x i32> [[TMP17]], zeroinitializer | |
| ; CHECK-NEXT: [[TMP19:%.*]] = insertvalue { <4 x i32>, <4 x i1> } poison, <4 x i32> [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP20:%.*]] = insertvalue { <4 x i32>, <4 x i1> } [[TMP19]], <4 x i1> [[TMP18]], 1 | |
| ; CHECK-NEXT: ret { <4 x i32>, <4 x i1> } [[TMP20]] | |
| ; | |
| %r = call { <4 x i32>, <4 x i1> } @llvm.umul.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) | |
| ret { <4 x i32>, <4 x i1> } %r | |
| } | |
| define { i32, i1 } @smul_i32(i32 %a, i32 %b) { | |
| ; CHECK-LABEL: define { i32, i1 } @smul_i32( | |
| ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[TMP2:%.*]] = call { i32, i32 } @llvm.dx.imul.i32(i32 [[A]], i32 [[B]]) | |
| ; CHECK-NEXT: [[TMP23:%.*]] = extractvalue { i32, i32 } [[TMP2]], 0 | |
| ; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { i32, i32 } [[TMP2]], 1 | |
| ; CHECK-NEXT: [[TMP24:%.*]] = ashr i32 [[TMP1]], 31 | |
| ; CHECK-NEXT: [[TMP25:%.*]] = icmp ne i32 [[TMP23]], [[TMP24]] | |
| ; CHECK-NEXT: [[TMP26:%.*]] = insertvalue { i32, i1 } poison, i32 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP27:%.*]] = insertvalue { i32, i1 } [[TMP26]], i1 [[TMP25]], 1 | |
| ; CHECK-NEXT: ret { i32, i1 } [[TMP27]] | |
| ; | |
| %r = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %a, i32 %b) | |
| ret { i32, i1 } %r | |
| } | |
| ; Narrow overloads widen to a 2*BW multiply, since that stays within 32 bits. | |
| define { i16, i1 } @umul_i16(i16 %a, i16 %b) { | |
| ; CHECK-LABEL: define { i16, i1 } @umul_i16( | |
| ; CHECK-SAME: i16 [[A:%.*]], i16 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = mul i16 [[A]], [[B]] | |
| ; CHECK-NEXT: [[TMP2:%.*]] = zext i16 [[A]] to i32 | |
| ; CHECK-NEXT: [[TMP3:%.*]] = zext i16 [[B]] to i32 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP2]], [[TMP3]] | |
| ; CHECK-NEXT: [[TMP5:%.*]] = lshr i32 [[TMP4]], 16 | |
| ; CHECK-NEXT: [[TMP6:%.*]] = icmp ne i32 [[TMP5]], 0 | |
| ; CHECK-NEXT: [[TMP7:%.*]] = insertvalue { i16, i1 } poison, i16 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP8:%.*]] = insertvalue { i16, i1 } [[TMP7]], i1 [[TMP6]], 1 | |
| ; CHECK-NEXT: ret { i16, i1 } [[TMP8]] | |
| ; | |
| %r = call { i16, i1 } @llvm.umul.with.overflow.i16(i16 %a, i16 %b) | |
| ret { i16, i1 } %r | |
| } | |
| define { i16, i1 } @smul_i16(i16 %a, i16 %b) { | |
| ; CHECK-LABEL: define { i16, i1 } @smul_i16( | |
| ; CHECK-SAME: i16 [[A:%.*]], i16 [[B:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = mul i16 [[A]], [[B]] | |
| ; CHECK-NEXT: [[TMP2:%.*]] = sext i16 [[A]] to i32 | |
| ; CHECK-NEXT: [[TMP3:%.*]] = sext i16 [[B]] to i32 | |
| ; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP2]], [[TMP3]] | |
| ; CHECK-NEXT: [[TMP5:%.*]] = sext i16 [[TMP1]] to i32 | |
| ; CHECK-NEXT: [[TMP6:%.*]] = icmp ne i32 [[TMP4]], [[TMP5]] | |
| ; CHECK-NEXT: [[TMP7:%.*]] = insertvalue { i16, i1 } poison, i16 [[TMP1]], 0 | |
| ; CHECK-NEXT: [[TMP8:%.*]] = insertvalue { i16, i1 } [[TMP7]], i1 [[TMP6]], 1 | |
| ; CHECK-NEXT: ret { i16, i1 } [[TMP8]] | |
| ; | |
| %r = call { i16, i1 } @llvm.smul.with.overflow.i16(i16 %a, i16 %b) | |
| ret { i16, i1 } %r | |
| } |