diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index ddaa44f43e63..1f247a9dafd3 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -2135,6 +2135,16 @@ static Value *simplifyAndInst(Value *Op0, Value *Op1, const SimplifyQuery &Q, if (Value *V = simplifyAndOrOfCmps(Q, Op0, Op1, true)) return V; + // zext(X) & sext(X) --> zext(X) + // sext(X) & zext(X) --> zext(X) + { + Value *X = nullptr; + if (match(Op0, m_ZExt(m_Value(X))) && match(Op1, m_SExt(m_Specific(X)))) + return Op0; + if (match(Op1, m_ZExt(m_Value(X))) && match(Op0, m_SExt(m_Specific(X)))) + return Op1; + } + // Try some generic simplifications for associative operations. if (Value *V = simplifyAssociativeBinOp(Instruction::And, Op0, Op1, Q, MaxRecurse)) @@ -2388,6 +2398,16 @@ static Value *simplifyOrInst(Value *Op0, Value *Op1, const SimplifyQuery &Q, } } + // zext(X) | sext(X) --> sext(X) + // sext(X) | zext(X) --> sext(X) + { + Value *X = nullptr; + if (match(Op0, m_ZExt(m_Value(X))) && match(Op1, m_SExt(m_Specific(X)))) + return Op1; + if (match(Op1, m_ZExt(m_Value(X))) && match(Op0, m_SExt(m_Specific(X)))) + return Op0; + } + // A funnel shift (rotate) can be decomposed into simpler shifts. See if we // are mixing in another shift that is redundant with the funnel shift. diff --git a/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll b/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll new file mode 100644 index 000000000000..525d85256936 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/fold-and-zext-sext.ll @@ -0,0 +1,101 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @use32(i32) + +define i32 @fold_zext_sext(i8 %x) { +; CHECK-LABEL: @fold_zext_sext( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: ret i32 [[ZEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + %and = and i32 %zext, %sext + ret i32 %and +} + +define i32 @fold_zext_sext_commuted(i8 %x) { +; CHECK-LABEL: @fold_zext_sext_commuted( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: ret i32 [[ZEXT]] +; + %sext = sext i8 %x to i32 + %zext = zext i8 %x to i32 + %and = and i32 %sext, %zext + ret i32 %and +} + +define <4 x i32> @fold_zext_sext_vec(<4 x i16> %x) { +; CHECK-LABEL: @fold_zext_sext_vec( +; CHECK-NEXT: [[ZEXT:%.*]] = zext <4 x i16> [[X:%.*]] to <4 x i32> +; CHECK-NEXT: ret <4 x i32> [[ZEXT]] +; + %zext = zext <4 x i16> %x to <4 x i32> + %sext = sext <4 x i16> %x to <4 x i32> + %and = and <4 x i32> %zext, %sext + ret <4 x i32> %and +} + +define i32 @multi_use_fold_zext_sext_1(i8 %x) { +; CHECK-LABEL: @multi_use_fold_zext_sext_1( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: call void @use32(i32 [[ZEXT]]) +; CHECK-NEXT: ret i32 [[ZEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + call void @use32(i32 %zext) + %and = and i32 %zext, %sext + ret i32 %and +} + +define i32 @multi_use_fold_zext_sext_2(i8 %x) { +; CHECK-LABEL: @multi_use_fold_zext_sext_2( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[X]] to i32 +; CHECK-NEXT: call void @use32(i32 [[SEXT]]) +; CHECK-NEXT: ret i32 [[ZEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + call void @use32(i32 %sext) + %and = and i32 %zext, %sext + ret i32 %and +} + +define i32 @neg_different_sources(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_different_sources( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[R:%.*]] = and i32 [[ZEXT]], [[SEXT]] +; CHECK-NEXT: ret i32 [[R]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %y to i32 + %and = and i32 %zext, %sext + ret i32 %and +} + +define i32 @neg_zext_only(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_zext_only( +; CHECK-NEXT: [[R1:%.*]] = and i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[R1]] to i32 +; CHECK-NEXT: ret i32 [[R]] +; + %z0 = zext i8 %x to i32 + %z1 = zext i8 %y to i32 + %and = and i32 %z0, %z1 + ret i32 %and +} + +define i32 @neg_sext_only(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_sext_only( +; CHECK-NEXT: [[R1:%.*]] = and i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = sext i8 [[R1]] to i32 +; CHECK-NEXT: ret i32 [[R]] +; + %s0 = sext i8 %x to i32 + %s1 = sext i8 %y to i32 + %and = and i32 %s0, %s1 + ret i32 %and +} diff --git a/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll new file mode 100644 index 000000000000..8ccfa4802206 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/fold-or-zext-sext.ll @@ -0,0 +1,101 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @use32(i32) + +define i32 @fold_zext_sext(i8 %x) { +; CHECK-LABEL: @fold_zext_sext( +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32 +; CHECK-NEXT: ret i32 [[SEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + %or = or i32 %zext, %sext + ret i32 %or +} + +define i32 @fold_zext_sext_commuted(i8 %x) { +; CHECK-LABEL: @fold_zext_sext_commuted( +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32 +; CHECK-NEXT: ret i32 [[SEXT]] +; + %sext = sext i8 %x to i32 + %zext = zext i8 %x to i32 + %or = or i32 %sext, %zext + ret i32 %or +} + +define <4 x i32> @fold_zext_sext_vec(<4 x i16> %x) { +; CHECK-LABEL: @fold_zext_sext_vec( +; CHECK-NEXT: [[SEXT:%.*]] = sext <4 x i16> [[X:%.*]] to <4 x i32> +; CHECK-NEXT: ret <4 x i32> [[SEXT]] +; + %zext = zext <4 x i16> %x to <4 x i32> + %sext = sext <4 x i16> %x to <4 x i32> + %or = or <4 x i32> %zext, %sext + ret <4 x i32> %or +} + +define i32 @multi_use_fold_zext_sext_1(i8 %x) { +; CHECK-LABEL: @multi_use_fold_zext_sext_1( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[X]] to i32 +; CHECK-NEXT: call void @use32(i32 [[ZEXT]]) +; CHECK-NEXT: ret i32 [[SEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + call void @use32(i32 %zext) + %or = or i32 %zext, %sext + ret i32 %or +} + +define i32 @multi_use_fold_zext_sext_2(i8 %x) { +; CHECK-LABEL: @multi_use_fold_zext_sext_2( +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[X:%.*]] to i32 +; CHECK-NEXT: call void @use32(i32 [[SEXT]]) +; CHECK-NEXT: ret i32 [[SEXT]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %x to i32 + call void @use32(i32 %sext) + %or = or i32 %zext, %sext + ret i32 %or +} + +define i32 @neg_different_sources(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_different_sources( +; CHECK-NEXT: [[ZEXT:%.*]] = zext i8 [[X:%.*]] to i32 +; CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[R:%.*]] = or i32 [[ZEXT]], [[SEXT]] +; CHECK-NEXT: ret i32 [[R]] +; + %zext = zext i8 %x to i32 + %sext = sext i8 %y to i32 + %or = or i32 %zext, %sext + ret i32 %or +} + +define i32 @neg_zext_only(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_zext_only( +; CHECK-NEXT: [[R1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[R1]] to i32 +; CHECK-NEXT: ret i32 [[R]] +; + %z0 = zext i8 %x to i32 + %z1 = zext i8 %y to i32 + %or = or i32 %z0, %z1 + ret i32 %or +} + +define i32 @neg_sext_only(i8 %x, i8 %y) { +; CHECK-LABEL: @neg_sext_only( +; CHECK-NEXT: [[R1:%.*]] = or i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = sext i8 [[R1]] to i32 +; CHECK-NEXT: ret i32 [[R]] +; + %s0 = sext i8 %x to i32 + %s1 = sext i8 %y to i32 + %or = or i32 %s0, %s1 + ret i32 %or +}