From f43438b6c3e8cb979658c87baf681f2e24aff9df Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Thu, 24 Apr 2014 08:15:31 +0000 Subject: [PATCH] X86: Emit test instead of constant shift + compare if the shift result is unused. This allows us to compile return (mask & 0x8 ? a : b); into testb $8, %dil cmovnel %edx, %esi instead of andl $8, %edi shrl $3, %edi cmovnel %edx, %esi which we formed previously because dag combiner canonicalizes setcc of and into shift. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@207088 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/X86/X86ISelLowering.cpp | 66 ++++++++++++++++++++---------- test/CodeGen/X86/cmp.ll | 34 ++++++++++++--- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index b59861f033e..f40ad30e658 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -9668,6 +9668,25 @@ static SDValue LowerVectorAllZeroTest(SDValue Op, const X86Subtarget *Subtarget, VecIns.back(), VecIns.back()); } +/// \brief return true if \c Op has a use that doesn't just read flags. +static bool hasNonFlagsUse(SDValue Op) { + for (SDNode::use_iterator UI = Op->use_begin(), UE = Op->use_end(); UI != UE; + ++UI) { + SDNode *User = *UI; + unsigned UOpNo = UI.getOperandNo(); + if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse()) { + // Look pass truncate. + UOpNo = User->use_begin().getOperandNo(); + User = *User->use_begin(); + } + + if (User->getOpcode() != ISD::BRCOND && User->getOpcode() != ISD::SETCC && + !(User->getOpcode() == ISD::SELECT && UOpNo == 0)) + return true; + } + return false; +} + /// Emit nodes that will be selected as "test Op0,Op0", or something /// equivalent. SDValue X86TargetLowering::EmitTest(SDValue Op, unsigned X86CC, SDLoc dl, @@ -9772,31 +9791,34 @@ SDValue X86TargetLowering::EmitTest(SDValue Op, unsigned X86CC, SDLoc dl, Opcode = X86ISD::ADD; NumOperands = 2; break; - case ISD::AND: { + case ISD::SHL: + case ISD::SRL: + // If we have a constant logical shift that's only used in a comparison + // against zero turn it into an equivalent AND. This allows turning it into + // a TEST instruction later. + if (isa(Op->getOperand(1)) && !hasNonFlagsUse(Op)) { + EVT VT = Op.getValueType(); + unsigned BitWidth = VT.getSizeInBits(); + unsigned ShAmt = Op->getConstantOperandVal(1); + if (ShAmt >= BitWidth) // Avoid undefined shifts. + break; + APInt Mask = ArithOp.getOpcode() == ISD::SRL + ? APInt::getHighBitsSet(BitWidth, BitWidth - ShAmt) + : APInt::getLowBitsSet(BitWidth, BitWidth - ShAmt); + if (!Mask.isSignedIntN(32)) // Avoid large immediates. + break; + SDValue New = DAG.getNode(ISD::AND, dl, VT, Op->getOperand(0), + DAG.getConstant(Mask, VT)); + DAG.ReplaceAllUsesWith(Op, New); + Op = New; + } + break; + + case ISD::AND: // If the primary and result isn't used, don't bother using X86ISD::AND, // because a TEST instruction will be better. - bool NonFlagUse = false; - for (SDNode::use_iterator UI = Op.getNode()->use_begin(), - UE = Op.getNode()->use_end(); UI != UE; ++UI) { - SDNode *User = *UI; - unsigned UOpNo = UI.getOperandNo(); - if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse()) { - // Look pass truncate. - UOpNo = User->use_begin().getOperandNo(); - User = *User->use_begin(); - } - - if (User->getOpcode() != ISD::BRCOND && - User->getOpcode() != ISD::SETCC && - !(User->getOpcode() == ISD::SELECT && UOpNo == 0)) { - NonFlagUse = true; - break; - } - } - - if (!NonFlagUse) + if (!hasNonFlagsUse(Op)) break; - } // FALL THROUGH case ISD::SUB: case ISD::OR: diff --git a/test/CodeGen/X86/cmp.ll b/test/CodeGen/X86/cmp.ll index 551d9bc6074..c52197f9be4 100644 --- a/test/CodeGen/X86/cmp.ll +++ b/test/CodeGen/X86/cmp.ll @@ -26,9 +26,22 @@ cond_true: ; preds = %0 ReturnBlock: ; preds = %0 ret i32 0 ; CHECK-LABEL: test2: -; CHECK: movl (%rsi), %eax -; CHECK: shll $3, %eax -; CHECK: testl %eax, %eax +; CHECK: testl $536870911, (%rsi) +} + +define i8 @test2b(i8 %X, i8* %y) nounwind { + %tmp = load i8* %y ; [#uses=1] + %tmp1 = shl i8 %tmp, 3 ; [#uses=1] + %tmp1.upgrd.2 = icmp eq i8 %tmp1, 0 ; [#uses=1] + br i1 %tmp1.upgrd.2, label %ReturnBlock, label %cond_true + +cond_true: ; preds = %0 + ret i8 1 + +ReturnBlock: ; preds = %0 + ret i8 0 +; CHECK-LABEL: test2b: +; CHECK: testb $31, (%rsi) } define i64 @test3(i64 %x) nounwind { @@ -68,8 +81,8 @@ define i32 @test5(double %A) nounwind { bb12:; preds = %entry ret i32 32 ; CHECK-LABEL: test5: -; CHECK: ucomisd LCPI4_0(%rip), %xmm0 -; CHECK: ucomisd LCPI4_1(%rip), %xmm0 +; CHECK: ucomisd LCPI5_0(%rip), %xmm0 +; CHECK: ucomisd LCPI5_1(%rip), %xmm0 } declare i32 @foo(...) @@ -163,3 +176,14 @@ define i32 @test12() uwtable ssp { } declare zeroext i1 @test12b() + +define i32 @test13(i32 %mask, i32 %base, i32 %intra) { + %and = and i32 %mask, 8 + %tobool = icmp ne i32 %and, 0 + %cond = select i1 %tobool, i32 %intra, i32 %base + ret i32 %cond + +; CHECK-LABEL: test13: +; CHECK: testb $8, %dil +; CHECK: cmovnel +}