From 6882d9adb4094b3122a49d287b0d944405bbc763 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Sat, 24 Jun 2023 10:35:17 -0400 Subject: [PATCH] SeparateConstOffsetForGEP: Fill out some missing test coverage 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. --- .../AMDGPU/reunite-exts-source-types.ll | 70 ++ .../AMDGPU/reunite-exts.ll | 955 +++++++++++++++++++++ 2 files changed, 1025 insertions(+) create mode 100644 llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts-source-types.ll create mode 100644 llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts.ll 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 index 0000000..1e88108 --- /dev/null +++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts-source-types.ll @@ -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 index 0000000..28543f6 --- /dev/null +++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reunite-exts.ll @@ -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 +} -- 2.7.4