SeparateConstOffsetForGEP: Fill out some missing test coverage
authorMatt Arsenault <Matthew.Arsenault@amd.com>
Sat, 24 Jun 2023 14:35:17 +0000 (10:35 -0400)
committerMatt Arsenault <Matthew.Arsenault@amd.com>
Mon, 26 Jun 2023 17:58:06 +0000 (13:58 -0400)
Try to test several untested paths.
 - Test the extension source type check
 - Test the programUndefinedIfPoison check
 - Test the add/sub with commuted operands
 - Test with vectors
 - Test multiple uses
 - Try to break operand map mismatches
 - Add some preparatory tests for zext+nuw support.

llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts-source-types.ll [new file with mode: 0644]
llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts.ll [new file with mode: 0644]

diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts-source-types.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts-source-types.ll
new file mode 100644 (file)
index 0000000..1e88108
--- /dev/null
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -passes=separate-const-offset-from-gep,gvn \
+; RUN:     -reassociate-geps-verify-no-dead-code < %s | FileCheck %s
+
+; Make sure there's no assertion when an add/sub with two extended
+; source operands have different source types.
+
+define i64 @add_sext_different_types(i16 %arg.i16, i32 %arg.i32) {
+; CHECK-LABEL: define i64 @add_sext_different_types
+; CHECK-SAME: (i16 [[ARG_I16:%.*]], i32 [[ARG_I32:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X1_SEXT:%.*]] = sext i32 [[ARG_I32]] to i64
+; CHECK-NEXT:    [[Y1_SEXT:%.*]] = sext i16 [[ARG_I16]] to i64
+; CHECK-NEXT:    [[XY12:%.*]] = add i64 [[X1_SEXT]], [[Y1_SEXT]]
+; CHECK-NEXT:    ret i64 [[XY12]]
+;
+entry:
+  %x1.sext = sext i32 %arg.i32 to i64
+  %y1.sext = sext i16 %arg.i16 to i64
+  %xy12 = add i64 %x1.sext, %y1.sext
+  ret i64 %xy12
+}
+
+define i64 @sub_sext_different_types(i16 %arg.i16, i32 %arg.i32) {
+; CHECK-LABEL: define i64 @sub_sext_different_types
+; CHECK-SAME: (i16 [[ARG_I16:%.*]], i32 [[ARG_I32:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X1_SEXT:%.*]] = sext i32 [[ARG_I32]] to i64
+; CHECK-NEXT:    [[Y1_SEXT:%.*]] = sext i16 [[ARG_I16]] to i64
+; CHECK-NEXT:    [[XY12:%.*]] = sub i64 [[X1_SEXT]], [[Y1_SEXT]]
+; CHECK-NEXT:    ret i64 [[XY12]]
+;
+entry:
+  %x1.sext = sext i32 %arg.i32 to i64
+  %y1.sext = sext i16 %arg.i16 to i64
+  %xy12 = sub i64 %x1.sext, %y1.sext
+  ret i64 %xy12
+}
+
+define i64 @add_zext_different_types(i16 %arg.i16, i32 %arg.i32) {
+; CHECK-LABEL: define i64 @add_zext_different_types
+; CHECK-SAME: (i16 [[ARG_I16:%.*]], i32 [[ARG_I32:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X1_ZEXT:%.*]] = zext i32 [[ARG_I32]] to i64
+; CHECK-NEXT:    [[Y1_ZEXT:%.*]] = zext i16 [[ARG_I16]] to i64
+; CHECK-NEXT:    [[XY12:%.*]] = add i64 [[X1_ZEXT]], [[Y1_ZEXT]]
+; CHECK-NEXT:    ret i64 [[XY12]]
+;
+entry:
+  %x1.zext = zext i32 %arg.i32 to i64
+  %y1.zext = zext i16 %arg.i16 to i64
+  %xy12 = add i64 %x1.zext, %y1.zext
+  ret i64 %xy12
+}
+
+define i64 @sub_zext_different_types(i16 %arg.i16, i32 %arg.i32) {
+; CHECK-LABEL: define i64 @sub_zext_different_types
+; CHECK-SAME: (i16 [[ARG_I16:%.*]], i32 [[ARG_I32:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X1_ZEXT:%.*]] = zext i32 [[ARG_I32]] to i64
+; CHECK-NEXT:    [[Y1_ZEXT:%.*]] = zext i16 [[ARG_I16]] to i64
+; CHECK-NEXT:    [[XY12:%.*]] = sub i64 [[X1_ZEXT]], [[Y1_ZEXT]]
+; CHECK-NEXT:    ret i64 [[XY12]]
+;
+entry:
+  %x1.zext = zext i32 %arg.i32 to i64
+  %y1.zext = zext i16 %arg.i16 to i64
+  %xy12 = sub i64 %x1.zext, %y1.zext
+  ret i64 %xy12
+}
diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts.ll
new file mode 100644 (file)
index 0000000..28543f6
--- /dev/null
@@ -0,0 +1,955 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -passes=separate-const-offset-from-gep < %s | FileCheck %s
+
+declare void @use.i32(i32 noundef)
+declare void @allow.undef.use.i32(i32)
+
+declare void @use.v2i32(<2 x i32> noundef)
+declare void @allow.undef.use.v2i32(<2 x i32>)
+
+declare void @use.i64(i64 noundef)
+
+; Should fold out the 64-bit add
+define i64 @add_sext__dominating_add_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NSW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+; Should fold out the 64-bit sub
+define i64 @sub_sext__dominating_sub_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sext i32 [[SUB_NSW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; Should fold out the 64-bit add. The 64-bit add has commuted operands
+; compared to the original.
+define i64 @add_sext__dominating_add_nsw_commuted(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nsw_commuted
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NSW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg1.sext, %arg0.sext
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+; The second sub has commuted operands
+define i64 @sub_sext__dominating_sub_nsw_commuted(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nsw_commuted
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG1_SEXT]], [[ARG0_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg1.sext, %arg0.sext
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; Missing nsw on the add i32, can't do anything
+define i64 @add_sext__dominating_add(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+; Missing nsw on the sub i32, can't do anything
+define i64 @sub_sext__dominating_sub(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; The use of the 32-bit add allows poison, so can't fold.
+define i64 @add_sext__dominating_add_nsw_defined(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nsw_defined
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @allow.undef.use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @allow.undef.use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+; The use of the 32-bit sub allows poison, so can't fold.
+define i64 @sub_sext__dominating_sub_nsw_defined(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nsw_defined
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @allow.undef.use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @allow.undef.use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+define i64 @add_sext__dominating_add_nsw_multi_use_sext0(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nsw_multi_use_sext0
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i64(i64 [[ARG0_SEXT]])
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i64(i64 %arg0.sext)
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+define i64 @add_sext__dominating_add_nsw_multi_use_sext1(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nsw_multi_use_sext1
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i64(i64 [[ARG1_SEXT]])
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i64(i64 %arg1.sext)
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+define i64 @sub_sext__dominating_sub_nsw_multi_use_sext0(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nsw_multi_use_sext0
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i64(i64 [[ARG0_SEXT]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i64(i64 %arg0.sext)
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+define i64 @sub_sext__dominating_sub_nsw_multi_use_sext1(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nsw_multi_use_sext1
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i64(i64 [[ARG1_SEXT]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i64(i64 %arg1.sext)
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; --------------------------------------------------------------------
+; Vector handling
+; --------------------------------------------------------------------
+
+; Should fold out the 64-bit add
+define <2 x i64> @add_sext__dominating_add_nsw_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @add_sext__dominating_add_nsw_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[ADD_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %add.sext = add <2 x i64> %arg0.sext, %arg1.sext
+  call void @use.v2i32(<2 x i32> %add.nsw)
+  ret <2 x i64> %add.sext
+}
+
+; Should fold out the 64-bit sub
+define <2 x i64> @sub_sext__dominating_sub_nsw_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @sub_sext__dominating_sub_nsw_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[SUB_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %sub.sext = sub <2 x i64> %arg0.sext, %arg1.sext
+  call void @use.v2i32(<2 x i32> %sub.nsw)
+  ret <2 x i64> %sub.sext
+}
+
+; Should fold out the 64-bit add. The 64-bit add has commuted operands
+; compared to the original.
+define <2 x i64> @add_sext__dominating_add_nsw_commuted_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @add_sext__dominating_add_nsw_commuted_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add <2 x i64> [[ARG1_SEXT]], [[ARG0_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[ADD_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %add.sext = add <2 x i64> %arg1.sext, %arg0.sext
+  call void @use.v2i32(<2 x i32> %add.nsw)
+  ret <2 x i64> %add.sext
+}
+
+; The second sub has commuted operands
+define <2 x i64> @sub_sext__dominating_sub_nsw_commuted_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @sub_sext__dominating_sub_nsw_commuted_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub <2 x i64> [[ARG1_SEXT]], [[ARG0_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[SUB_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %sub.sext = sub <2 x i64> %arg1.sext, %arg0.sext
+  call void @use.v2i32(<2 x i32> %sub.nsw)
+  ret <2 x i64> %sub.sext
+}
+
+; Missing nsw on the add <2 x i32>, can't do anything
+define <2 x i64> @add_sext__dominating_add_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @add_sext__dominating_add_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[ADD_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %add.sext = add <2 x i64> %arg0.sext, %arg1.sext
+  call void @use.v2i32(<2 x i32> %add.nsw)
+  ret <2 x i64> %add.sext
+}
+
+; Missing nsw on the sub <2 x i32>, can't do anything
+define <2 x i64> @sub_sext__dominating_sub_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @sub_sext__dominating_sub_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[SUB_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %sub.sext = sub <2 x i64> %arg0.sext, %arg1.sext
+  call void @use.v2i32(<2 x i32> %sub.nsw)
+  ret <2 x i64> %sub.sext
+}
+
+; The use of the 32-bit add allows poison, so can't fold.
+define <2 x i64> @add_sext__dominating_add_nsw_defined_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @add_sext__dominating_add_nsw_defined_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @allow.undef.use.v2i32(<2 x i32> [[ADD_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %add.sext = add <2 x i64> %arg0.sext, %arg1.sext
+  call void @allow.undef.use.v2i32(<2 x i32> %add.nsw)
+  ret <2 x i64> %add.sext
+}
+
+; The use of the 32-bit sub allows poison, so can't fold.
+define <2 x i64> @sub_sext__dominating_sub_nsw_defined_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @sub_sext__dominating_sub_nsw_defined_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub <2 x i64> [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @allow.undef.use.v2i32(<2 x i32> [[SUB_NSW]])
+; CHECK-NEXT:    ret <2 x i64> [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw <2 x i32> %arg0, %arg1
+  %arg0.sext = sext <2 x i32> %arg0 to <2 x i64>
+  %arg1.sext = sext <2 x i32> %arg1 to <2 x i64>
+  %sub.sext = sub <2 x i64> %arg0.sext, %arg1.sext
+  call void @allow.undef.use.v2i32(<2 x i32> %sub.nsw)
+  ret <2 x i64> %sub.sext
+}
+
+; --------------------------------------------------------------------
+; Zext x nsw
+; --------------------------------------------------------------------
+
+; Want sext, not zext
+define i64 @add_zext__dominating_add_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_zext__dominating_add_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = zext i32 %arg0 to i64
+  %arg1.sext = zext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %add.sext
+}
+
+; Want sext, not zext
+define i64 @sub_zext__dominating_add_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_zext__dominating_add_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = zext i32 %arg0 to i64
+  %arg1.sext = zext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; --------------------------------------------------------------------
+; sext x nuw
+; --------------------------------------------------------------------
+
+; Want nsw, not nuw
+define i64 @add_sext__dominating_add_nuw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nuw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW:%.*]] = add nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nuw = add nuw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nuw)
+  ret i64 %add.sext
+}
+
+; Want nsw, not nuw
+define i64 @sub_sext__dominating_sub_nuw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nuw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nuw = sub nuw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nuw)
+  ret i64 %sub.sext
+}
+
+; --------------------------------------------------------------------
+; Misc negative tests
+; --------------------------------------------------------------------
+
+; Opcode mismatch 1
+define i64 @add_sext__dominating_sub_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_sub_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nsw)
+  ret i64 %sub.sext
+}
+
+; Opcode mismatch 2
+define i64 @sub_sext__dominating_add_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_add_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nsw)
+  ret i64 %sub.sext
+}
+
+; Both nsw add and sub coexist for the same inputs
+define void @add_sext__dominating_add_nsw_sub_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define void @add_sext__dominating_add_nsw_sub_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NSW]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    call void @use.i64(i64 [[ADD_SEXT]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_SEXT]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nsw)
+  call void @use.i32(i32 %sub.nsw)
+  call void @use.i64(i64 %add.sext)
+  call void @use.i64(i64 %sub.sext)
+  ret void
+}
+
+; Both nsw add and sub coexist for the same inputs, but commuted
+define void @add_sext__dominating_add_nsw_sub_nsw_swapped(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define void @add_sext__dominating_add_nsw_sub_nsw_swapped
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG1]], [[ARG0]]
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NSW]] to i64
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG1_SEXT]], [[ARG0_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    call void @use.i64(i64 [[ADD_SEXT]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_SEXT]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %sub.nsw = sub nsw i32 %arg1, %arg0
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  %sub.sext = sub i64 %arg1.sext, %arg0.sext
+  call void @use.i32(i32 %add.nsw)
+  call void @use.i32(i32 %sub.nsw)
+  call void @use.i64(i64 %add.sext)
+  call void @use.i64(i64 %sub.sext)
+  ret void
+}
+
+; --------------------------------------------------------------------
+; zext x nuw
+; --------------------------------------------------------------------
+
+; Should fold out the add i64
+define i64 @add_zext__dominating_add_nuw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_zext__dominating_add_nuw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW:%.*]] = add nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_ZEXT:%.*]] = add i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW]])
+; CHECK-NEXT:    ret i64 [[ADD_ZEXT]]
+;
+entry:
+  %add.nuw = add nuw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %add.zext = add i64 %arg0.zext, %arg1.zext
+  call void @use.i32(i32 %add.nuw)
+  ret i64 %add.zext
+}
+
+; Should fold out the sub i64
+define i64 @sub_zext__dominating_sub_nuw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_zext__dominating_sub_nuw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_ZEXT:%.*]] = sub i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    ret i64 [[SUB_ZEXT]]
+;
+entry:
+  %sub.nuw = sub nuw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %sub.zext = sub i64 %arg0.zext, %arg1.zext
+  call void @use.i32(i32 %sub.nuw)
+  ret i64 %sub.zext
+}
+
+; Should fold out the add <2 x i64>
+define <2 x i64> @add_zext__dominating_add_nuw_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @add_zext__dominating_add_nuw_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW:%.*]] = add nuw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[ADD_ZEXT:%.*]] = add <2 x i64> [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[ADD_NUW]])
+; CHECK-NEXT:    ret <2 x i64> [[ADD_ZEXT]]
+;
+entry:
+  %add.nuw = add nuw <2 x i32> %arg0, %arg1
+  %arg0.zext = zext <2 x i32> %arg0 to <2 x i64>
+  %arg1.zext = zext <2 x i32> %arg1 to <2 x i64>
+  %add.zext = add <2 x i64> %arg0.zext, %arg1.zext
+  call void @use.v2i32(<2 x i32> %add.nuw)
+  ret <2 x i64> %add.zext
+}
+
+; Should fold out the sub <2 x i64>
+define <2 x i64> @sub_zext__dominating_sub_nuw_vector(<2 x i32> %arg0, <2 x i32> %arg1) {
+; CHECK-LABEL: define <2 x i64> @sub_zext__dominating_sub_nuw_vector
+; CHECK-SAME: (<2 x i32> [[ARG0:%.*]], <2 x i32> [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw <2 x i32> [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext <2 x i32> [[ARG0]] to <2 x i64>
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext <2 x i32> [[ARG1]] to <2 x i64>
+; CHECK-NEXT:    [[SUB_ZEXT:%.*]] = sub <2 x i64> [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.v2i32(<2 x i32> [[SUB_NUW]])
+; CHECK-NEXT:    ret <2 x i64> [[SUB_ZEXT]]
+;
+entry:
+  %sub.nuw = sub nuw <2 x i32> %arg0, %arg1
+  %arg0.zext = zext <2 x i32> %arg0 to <2 x i64>
+  %arg1.zext = zext <2 x i32> %arg1 to <2 x i64>
+  %sub.zext = sub <2 x i64> %arg0.zext, %arg1.zext
+  call void @use.v2i32(<2 x i32> %sub.nuw)
+  ret <2 x i64> %sub.zext
+}
+
+; Both nuw and nsw exist
+define void @add_zext_add_sext__dominating_add_nuw_add_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define void @add_zext_add_sext__dominating_add_nuw_add_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW:%.*]] = add nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ADD_NSW:%.*]] = add nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_ZEXT:%.*]] = add i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = add i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW]])
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NSW]])
+; CHECK-NEXT:    call void @use.i64(i64 [[ADD_ZEXT]])
+; CHECK-NEXT:    call void @use.i64(i64 [[ADD_SEXT]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %add.nuw = add nuw i32 %arg0, %arg1
+  %add.nsw = add nsw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.zext = add i64 %arg0.zext, %arg1.zext
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nuw)
+  call void @use.i32(i32 %add.nsw)
+  call void @use.i64(i64 %add.zext)
+  call void @use.i64(i64 %add.sext)
+  ret void
+}
+
+; Both exist
+define void @sub_zext_sub_sext__dominating_sub_nuw_sub_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define void @sub_zext_sub_sext__dominating_sub_nuw_sub_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_ZEXT:%.*]] = sub i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_ZEXT]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_SEXT]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %sub.nuw = sub nuw i32 %arg0, %arg1
+  %sub.nsw = sub nsw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.zext = sub i64 %arg0.zext, %arg1.zext
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nuw)
+  call void @use.i32(i32 %sub.nsw)
+  call void @use.i64(i64 %sub.zext)
+  call void @use.i64(i64 %sub.sext)
+  ret void
+}
+
+; Both exist with commuted operands from each other
+define void @sub_zext_sub_sext__dominating_sub_nuw_sub_nsw_commuted(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define void @sub_zext_sub_sext__dominating_sub_nuw_sub_nsw_commuted
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_NSW:%.*]] = sub nsw i32 [[ARG1]], [[ARG0]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ARG0_SEXT:%.*]] = sext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_SEXT:%.*]] = sext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_ZEXT:%.*]] = sub i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sub i64 [[ARG0_SEXT]], [[ARG1_SEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NSW]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_ZEXT]])
+; CHECK-NEXT:    call void @use.i64(i64 [[SUB_SEXT]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %sub.nuw = sub nuw i32 %arg0, %arg1
+  %sub.nsw = sub nsw i32 %arg1, %arg0
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.zext = sub i64 %arg0.zext, %arg1.zext
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nuw)
+  call void @use.i32(i32 %sub.nsw)
+  call void @use.i64(i64 %sub.zext)
+  call void @use.i64(i64 %sub.sext)
+  ret void
+}
+
+; --------------------------------------------------------------------
+; zext x nuw+nsw
+; --------------------------------------------------------------------
+
+; Should fold out the add i64
+define i64 @add_zext__dominating_add_nuw_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_zext__dominating_add_nuw_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW_NSW:%.*]] = add nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_ZEXT:%.*]] = add i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_ZEXT]]
+;
+entry:
+  %add.nuw.nsw = add nuw nsw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %add.zext = add i64 %arg0.zext, %arg1.zext
+  call void @use.i32(i32 %add.nuw.nsw)
+  ret i64 %add.zext
+}
+
+; Should fold out the add i64
+define i64 @add_zext__dominating_add_nuw_nsw_commute(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_zext__dominating_add_nuw_nsw_commute
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW_NSW:%.*]] = add nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[ADD_ZEXT:%.*]] = add i64 [[ARG1_ZEXT]], [[ARG0_ZEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_ZEXT]]
+;
+entry:
+  %add.nuw.nsw = add nuw nsw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %add.zext = add i64 %arg1.zext, %arg0.zext
+  call void @use.i32(i32 %add.nuw.nsw)
+  ret i64 %add.zext
+}
+
+; Should fold out the sub i64
+define i64 @sub_zext__dominating_sub_nuw_esw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_zext__dominating_sub_nuw_esw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ARG0_ZEXT:%.*]] = zext i32 [[ARG0]] to i64
+; CHECK-NEXT:    [[ARG1_ZEXT:%.*]] = zext i32 [[ARG1]] to i64
+; CHECK-NEXT:    [[SUB_ZEXT:%.*]] = sub i64 [[ARG0_ZEXT]], [[ARG1_ZEXT]]
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    ret i64 [[SUB_ZEXT]]
+;
+entry:
+  %sub.nuw = sub nuw nsw i32 %arg0, %arg1
+  %arg0.zext = zext i32 %arg0 to i64
+  %arg1.zext = zext i32 %arg1 to i64
+  %sub.zext = sub i64 %arg0.zext, %arg1.zext
+  call void @use.i32(i32 %sub.nuw)
+  ret i64 %sub.zext
+}
+
+; --------------------------------------------------------------------
+; sext x nuw+nsw
+; --------------------------------------------------------------------
+
+; Should fold out the add i64
+define i64 @add_sext__dominating_add_nuw_nsw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nuw_nsw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW_NSW:%.*]] = add nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NUW_NSW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nuw.nsw = add nuw nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %add.nuw.nsw)
+  ret i64 %add.sext
+}
+
+; Should fold out the add i64
+define i64 @add_sext__dominating_add_nuw_nsw_commute(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @add_sext__dominating_add_nuw_nsw_commute
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[ADD_NUW_NSW:%.*]] = add nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[ADD_SEXT:%.*]] = sext i32 [[ADD_NUW_NSW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[ADD_NUW_NSW]])
+; CHECK-NEXT:    ret i64 [[ADD_SEXT]]
+;
+entry:
+  %add.nuw.nsw = add nuw nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %add.sext = add i64 %arg1.sext, %arg0.sext
+  call void @use.i32(i32 %add.nuw.nsw)
+  ret i64 %add.sext
+}
+
+; Should fold out the sub i64
+define i64 @sub_sext__dominating_sub_nuw_esw(i32 %arg0, i32 %arg1) {
+; CHECK-LABEL: define i64 @sub_sext__dominating_sub_nuw_esw
+; CHECK-SAME: (i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB_NUW:%.*]] = sub nuw nsw i32 [[ARG0]], [[ARG1]]
+; CHECK-NEXT:    [[SUB_SEXT:%.*]] = sext i32 [[SUB_NUW]] to i64
+; CHECK-NEXT:    call void @use.i32(i32 [[SUB_NUW]])
+; CHECK-NEXT:    ret i64 [[SUB_SEXT]]
+;
+entry:
+  %sub.nuw = sub nuw nsw i32 %arg0, %arg1
+  %arg0.sext = sext i32 %arg0 to i64
+  %arg1.sext = sext i32 %arg1 to i64
+  %sub.sext = sub i64 %arg0.sext, %arg1.sext
+  call void @use.i32(i32 %sub.nuw)
+  ret i64 %sub.sext
+}