From afa192cfb6049a15c5542d132d500b910b802c74 Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Thu, 9 Jun 2022 16:59:26 -0400 Subject: [PATCH] [InstCombine] add narrowing transform for low-masked binop with zext operand https://alive2.llvm.org/ce/z/hRy3rE As shown in D123408, we can produce this pattern when moving cast around, and we already have a related fold for a binop with a constant operand. --- .../InstCombine/InstCombineAndOrXor.cpp | 23 +++++++++- llvm/test/Transforms/InstCombine/and.ll | 44 ++++++++++++------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp index 9db113aa2b92..01097a0fd0c1 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1869,6 +1869,7 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) { }; BinaryOperator *BO; if (match(Op0, m_OneUse(m_BinOp(BO))) && isSuitableBinOpcode(BO)) { + Instruction::BinaryOps BOpcode = BO->getOpcode(); Value *X; const APInt *C1; // TODO: The one-use restrictions could be relaxed a little if the AND @@ -1878,12 +1879,30 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) { unsigned XWidth = X->getType()->getScalarSizeInBits(); Constant *TruncC1 = ConstantInt::get(X->getType(), C1->trunc(XWidth)); Value *BinOp = isa(BO->getOperand(0)) - ? Builder.CreateBinOp(BO->getOpcode(), X, TruncC1) - : Builder.CreateBinOp(BO->getOpcode(), TruncC1, X); + ? Builder.CreateBinOp(BOpcode, X, TruncC1) + : Builder.CreateBinOp(BOpcode, TruncC1, X); Constant *TruncC = ConstantInt::get(X->getType(), C->trunc(XWidth)); Value *And = Builder.CreateAnd(BinOp, TruncC); return new ZExtInst(And, Ty); } + + if (match(BO->getOperand(0), m_OneUse(m_ZExt(m_Value(X)))) && + C->isMask(X->getType()->getScalarSizeInBits())) { + Y = BO->getOperand(1); + Value *TrY = Builder.CreateTrunc(Y, X->getType(), Y->getName() + ".tr"); + Value *NewBO = + Builder.CreateBinOp(BOpcode, X, TrY, BO->getName() + ".narrow"); + return new ZExtInst(NewBO, Ty); + } + + if (match(BO->getOperand(1), m_OneUse(m_ZExt(m_Value(X)))) && + C->isMask(X->getType()->getScalarSizeInBits())) { + Y = BO->getOperand(0); + Value *TrY = Builder.CreateTrunc(Y, X->getType(), Y->getName() + ".tr"); + Value *NewBO = + Builder.CreateBinOp(BOpcode, TrY, X, BO->getName() + ".narrow"); + return new ZExtInst(NewBO, Ty); + } } } diff --git a/llvm/test/Transforms/InstCombine/and.ll b/llvm/test/Transforms/InstCombine/and.ll index c3bd7deead56..c06074c79fc5 100644 --- a/llvm/test/Transforms/InstCombine/and.ll +++ b/llvm/test/Transforms/InstCombine/and.ll @@ -744,9 +744,9 @@ define i64 @test39(i32 %X) { define i32 @lowmask_add_zext(i8 %x, i32 %y) { ; CHECK-LABEL: @lowmask_add_zext( -; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32 -; CHECK-NEXT: [[BO:%.*]] = add i32 [[ZX]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = and i32 [[BO]], 255 +; CHECK-NEXT: [[Y_TR:%.*]] = trunc i32 [[Y:%.*]] to i8 +; CHECK-NEXT: [[BO_NARROW:%.*]] = add i8 [[Y_TR]], [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[BO_NARROW]] to i32 ; CHECK-NEXT: ret i32 [[R]] ; %zx = zext i8 %x to i32 @@ -758,9 +758,9 @@ define i32 @lowmask_add_zext(i8 %x, i32 %y) { define i32 @lowmask_add_zext_commute(i16 %x, i32 %p) { ; CHECK-LABEL: @lowmask_add_zext_commute( ; CHECK-NEXT: [[Y:%.*]] = mul i32 [[P:%.*]], [[P]] -; CHECK-NEXT: [[ZX:%.*]] = zext i16 [[X:%.*]] to i32 -; CHECK-NEXT: [[BO:%.*]] = add i32 [[Y]], [[ZX]] -; CHECK-NEXT: [[R:%.*]] = and i32 [[BO]], 65535 +; CHECK-NEXT: [[Y_TR:%.*]] = trunc i32 [[Y]] to i16 +; CHECK-NEXT: [[BO_NARROW:%.*]] = add i16 [[Y_TR]], [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i16 [[BO_NARROW]] to i32 ; CHECK-NEXT: ret i32 [[R]] ; %y = mul i32 %p, %p ; thwart complexity-based canonicalization @@ -770,6 +770,8 @@ define i32 @lowmask_add_zext_commute(i16 %x, i32 %p) { ret i32 %r } +; negative test - the mask constant must match the zext source type + define i32 @lowmask_add_zext_wrong_mask(i8 %x, i32 %y) { ; CHECK-LABEL: @lowmask_add_zext_wrong_mask( ; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32 @@ -783,6 +785,8 @@ define i32 @lowmask_add_zext_wrong_mask(i8 %x, i32 %y) { ret i32 %r } +; negative test - extra use + define i32 @lowmask_add_zext_use1(i8 %x, i32 %y) { ; CHECK-LABEL: @lowmask_add_zext_use1( ; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32 @@ -798,6 +802,8 @@ define i32 @lowmask_add_zext_use1(i8 %x, i32 %y) { ret i32 %r } +; negative test - extra use + define i32 @lowmask_add_zext_use2(i8 %x, i32 %y) { ; CHECK-LABEL: @lowmask_add_zext_use2( ; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32 @@ -813,11 +819,13 @@ define i32 @lowmask_add_zext_use2(i8 %x, i32 %y) { ret i32 %r } +; vector splats work too + define <2 x i32> @lowmask_sub_zext(<2 x i4> %x, <2 x i32> %y) { ; CHECK-LABEL: @lowmask_sub_zext( -; CHECK-NEXT: [[ZX:%.*]] = zext <2 x i4> [[X:%.*]] to <2 x i32> -; CHECK-NEXT: [[BO:%.*]] = sub <2 x i32> [[ZX]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = and <2 x i32> [[BO]], +; CHECK-NEXT: [[Y_TR:%.*]] = trunc <2 x i32> [[Y:%.*]] to <2 x i4> +; CHECK-NEXT: [[BO_NARROW:%.*]] = sub <2 x i4> [[X:%.*]], [[Y_TR]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i4> [[BO_NARROW]] to <2 x i32> ; CHECK-NEXT: ret <2 x i32> [[R]] ; %zx = zext <2 x i4> %x to <2 x i32> @@ -826,11 +834,13 @@ define <2 x i32> @lowmask_sub_zext(<2 x i4> %x, <2 x i32> %y) { ret <2 x i32> %r } +; weird types are allowed + define i17 @lowmask_sub_zext_commute(i5 %x, i17 %y) { ; CHECK-LABEL: @lowmask_sub_zext_commute( -; CHECK-NEXT: [[ZX:%.*]] = zext i5 [[X:%.*]] to i17 -; CHECK-NEXT: [[BO:%.*]] = sub i17 [[Y:%.*]], [[ZX]] -; CHECK-NEXT: [[R:%.*]] = and i17 [[BO]], 31 +; CHECK-NEXT: [[Y_TR:%.*]] = trunc i17 [[Y:%.*]] to i5 +; CHECK-NEXT: [[BO_NARROW:%.*]] = sub i5 [[Y_TR]], [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i5 [[BO_NARROW]] to i17 ; CHECK-NEXT: ret i17 [[R]] ; %zx = zext i5 %x to i17 @@ -841,9 +851,9 @@ define i17 @lowmask_sub_zext_commute(i5 %x, i17 %y) { define i32 @lowmask_mul_zext(i8 %x, i32 %y) { ; CHECK-LABEL: @lowmask_mul_zext( -; CHECK-NEXT: [[ZX:%.*]] = zext i8 [[X:%.*]] to i32 -; CHECK-NEXT: [[BO:%.*]] = mul i32 [[ZX]], [[Y:%.*]] -; CHECK-NEXT: [[R:%.*]] = and i32 [[BO]], 255 +; CHECK-NEXT: [[Y_TR:%.*]] = trunc i32 [[Y:%.*]] to i8 +; CHECK-NEXT: [[BO_NARROW:%.*]] = mul i8 [[Y_TR]], [[X:%.*]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[BO_NARROW]] to i32 ; CHECK-NEXT: ret i32 [[R]] ; %zx = zext i8 %x to i32 @@ -852,6 +862,8 @@ define i32 @lowmask_mul_zext(i8 %x, i32 %y) { ret i32 %r } +; TODO: we could have narrowed the xor + define i32 @lowmask_xor_zext_commute(i8 %x, i32 %p) { ; CHECK-LABEL: @lowmask_xor_zext_commute( ; CHECK-NEXT: [[Y:%.*]] = mul i32 [[P:%.*]], [[P]] @@ -867,6 +879,8 @@ define i32 @lowmask_xor_zext_commute(i8 %x, i32 %p) { ret i32 %r } +; TODO: we could have narrowed the or + define i24 @lowmask_or_zext_commute(i16 %x, i24 %y) { ; CHECK-LABEL: @lowmask_or_zext_commute( ; CHECK-NEXT: [[ZX:%.*]] = zext i16 [[X:%.*]] to i24