From: Dan Gohman Date: Wed, 19 Aug 2009 18:16:17 +0000 (+0000) Subject: Add an x86 peep that narrows TEST instructions to forms that use X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=6a402dc952ccad3f8fd0d9e272dbdd261f50854e Add an x86 peep that narrows TEST instructions to forms that use a smaller encoding. These kinds of patterns are very frequent in sqlite3, for example. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@79439 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/CodeGen/SelectionDAG.h b/include/llvm/CodeGen/SelectionDAG.h index 47df8672f47..1e0b4f1e694 100644 --- a/include/llvm/CodeGen/SelectionDAG.h +++ b/include/llvm/CodeGen/SelectionDAG.h @@ -686,6 +686,11 @@ public: const std::vector &ResultTys, const SDValue *Ops, unsigned NumOps); + /// getTargetExtractSubreg - A convenience function for creating + /// TargetInstrInfo::EXTRACT_SUBREG nodes. + SDValue getTargetExtractSubreg(int SRIdx, DebugLoc DL, EVT VT, + SDValue Operand); + /// getNodeIfExists - Get the specified node if it's already available, or /// else return NULL. SDNode *getNodeIfExists(unsigned Opcode, SDVTList VTs, diff --git a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 75de2035796..f5de81b9425 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -4586,6 +4586,17 @@ SDNode *SelectionDAG::getTargetNode(unsigned Opcode, DebugLoc dl, return getNode(~Opcode, dl, ResultTys, Ops, NumOps).getNode(); } +/// getTargetExtractSubreg - A convenience function for creating +/// TargetInstrInfo::EXTRACT_SUBREG nodes. +SDValue +SelectionDAG::getTargetExtractSubreg(int SRIdx, DebugLoc DL, EVT VT, + SDValue Operand) { + SDValue SRIdxVal = getTargetConstant(SRIdx, MVT::i32); + SDNode *Subreg = getTargetNode(TargetInstrInfo::EXTRACT_SUBREG, DL, + VT, Operand, SRIdxVal); + return SDValue(Subreg, 0); +} + /// getNodeIfExists - Get the specified node if it's already available, or /// else return NULL. SDNode *SelectionDAG::getNodeIfExists(unsigned Opcode, SDVTList VTList, diff --git a/lib/Target/X86/X86ISelDAGToDAG.cpp b/lib/Target/X86/X86ISelDAGToDAG.cpp index 1f1b4fecf5e..a730a7805d7 100644 --- a/lib/Target/X86/X86ISelDAGToDAG.cpp +++ b/lib/Target/X86/X86ISelDAGToDAG.cpp @@ -1743,9 +1743,8 @@ SDNode *X86DAGToDAGISel::Select(SDValue N) { Result, CurDAG->getTargetConstant(8, MVT::i8)), 0); // Then truncate it down to i8. - SDValue SRIdx = CurDAG->getTargetConstant(X86::SUBREG_8BIT, MVT::i32); - Result = SDValue(CurDAG->getTargetNode(X86::EXTRACT_SUBREG, dl, - MVT::i8, Result, SRIdx), 0); + Result = CurDAG->getTargetExtractSubreg(X86::SUBREG_8BIT, dl, + MVT::i8, Result); } else { Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, HiReg, NVT, InFlag); @@ -1919,9 +1918,8 @@ SDNode *X86DAGToDAGISel::Select(SDValue N) { CurDAG->getTargetConstant(8, MVT::i8)), 0); // Then truncate it down to i8. - SDValue SRIdx = CurDAG->getTargetConstant(X86::SUBREG_8BIT, MVT::i32); - Result = SDValue(CurDAG->getTargetNode(X86::EXTRACT_SUBREG, dl, - MVT::i8, Result, SRIdx), 0); + Result = CurDAG->getTargetExtractSubreg(X86::SUBREG_8BIT, dl, + MVT::i8, Result); } else { Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl, HiReg, NVT, InFlag); @@ -1944,6 +1942,105 @@ SDNode *X86DAGToDAGISel::Select(SDValue N) { return NULL; } + case X86ISD::CMP: { + if (getenv("NOCMP")) break; + SDValue N0 = Node->getOperand(0); + SDValue N1 = Node->getOperand(1); + + // Look for (X86cmp (and $op, $imm), 0) and see if we can convert it to + // use a smaller encoding. + if (N0.getNode()->getOpcode() == ISD::AND && N0.getNode()->hasOneUse() && + N0.getValueType() != MVT::i8 && + X86::isZeroNode(N1)) { + ConstantSDNode *C = dyn_cast(N0.getNode()->getOperand(1)); + if (!C) break; + + // For example, convert "testl %eax, $8" to "testb %al, $8" + if ((C->getZExtValue() & ~UINT64_C(0xff)) == 0) { + SDValue Imm = CurDAG->getTargetConstant(C->getZExtValue(), MVT::i8); + SDValue Reg = N0.getNode()->getOperand(0); + + // On x86-32, only the ABCD registers have 8-bit subregisters. + if (!Subtarget->is64Bit()) { + TargetRegisterClass *TRC = 0; + switch (N0.getValueType().getSimpleVT().SimpleTy) { + case MVT::i32: TRC = &X86::GR32_ABCDRegClass; break; + case MVT::i16: TRC = &X86::GR16_ABCDRegClass; break; + default: llvm_unreachable("Unsupported TEST operand type!"); + } + SDValue RC = CurDAG->getTargetConstant(TRC->getID(), MVT::i32); + Reg = SDValue(CurDAG->getTargetNode(X86::COPY_TO_REGCLASS, dl, + Reg.getValueType(), Reg, RC), 0); + } + + // Extract the l-register. + SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::SUBREG_8BIT, dl, + MVT::i8, Reg); + + // Emit a testb. + return CurDAG->getTargetNode(X86::TEST8ri, dl, MVT::i32, Subreg, Imm); + } + + // For example, "testl %eax, $2048" to "testb %ah, $8". + if ((C->getZExtValue() & ~UINT64_C(0xff00)) == 0) { + // Shift the immediate right by 8 bits. + SDValue ShiftedImm = CurDAG->getTargetConstant(C->getZExtValue() >> 8, + MVT::i8); + SDValue Reg = N0.getNode()->getOperand(0); + + // Put the value in an ABCD register. + TargetRegisterClass *TRC = 0; + switch (N0.getValueType().getSimpleVT().SimpleTy) { + case MVT::i64: TRC = &X86::GR64_ABCDRegClass; break; + case MVT::i32: TRC = &X86::GR32_ABCDRegClass; break; + case MVT::i16: TRC = &X86::GR16_ABCDRegClass; break; + default: llvm_unreachable("Unsupported TEST operand type!"); + } + SDValue RC = CurDAG->getTargetConstant(TRC->getID(), MVT::i32); + Reg = SDValue(CurDAG->getTargetNode(X86::COPY_TO_REGCLASS, dl, + Reg.getValueType(), Reg, RC), 0); + + // Extract the h-register. + SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::SUBREG_8BIT_HI, dl, + MVT::i8, Reg); + + // Emit a testb. No special NOREX tricks are needed since there's + // only one GPR operand! + return CurDAG->getTargetNode(X86::TEST8ri, dl, MVT::i32, + Subreg, ShiftedImm); + } + + // For example, "testl %eax, $32776" to "testw %ax, $32776". + if ((C->getZExtValue() & ~UINT64_C(0xffff)) == 0 && + N0.getValueType() != MVT::i16) { + SDValue Imm = CurDAG->getTargetConstant(C->getZExtValue(), MVT::i16); + SDValue Reg = N0.getNode()->getOperand(0); + + // Extract the 16-bit subregister. + SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::SUBREG_16BIT, dl, + MVT::i16, Reg); + + // Emit a testw. + return CurDAG->getTargetNode(X86::TEST16ri, dl, MVT::i32, Subreg, Imm); + } + + // For example, "testq %rax, $268468232" to "testl %eax, $268468232". + if ((C->getZExtValue() & ~UINT64_C(0xffffffff)) == 0 && + N0.getValueType() == MVT::i64) { + SDValue Imm = CurDAG->getTargetConstant(C->getZExtValue(), MVT::i32); + SDValue Reg = N0.getNode()->getOperand(0); + + // Extract the 32-bit subregister. + SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::SUBREG_32BIT, dl, + MVT::i32, Reg); + + // Emit a testl. + return CurDAG->getTargetNode(X86::TEST32ri, dl, MVT::i32, Subreg, Imm); + } + } + break; + } + case ISD::DECLARE: { // Handle DECLARE nodes here because the second operand may have been // wrapped in X86ISD::Wrapper. diff --git a/test/CodeGen/X86/live-out-reg-info.ll b/test/CodeGen/X86/live-out-reg-info.ll index b6fb7dfc72c..ffaf3c171e6 100644 --- a/test/CodeGen/X86/live-out-reg-info.ll +++ b/test/CodeGen/X86/live-out-reg-info.ll @@ -1,4 +1,4 @@ -; RUN: llvm-as < %s | llc -march=x86-64 | grep testl +; RUN: llvm-as < %s | llc -march=x86-64 | grep {testb \[$\]1,} ; Make sure dagcombine doesn't eliminate the comparison due ; to an off-by-one bug with ComputeMaskedBits information. diff --git a/test/CodeGen/X86/test-shrink.ll b/test/CodeGen/X86/test-shrink.ll new file mode 100644 index 00000000000..bbf727a45a5 --- /dev/null +++ b/test/CodeGen/X86/test-shrink.ll @@ -0,0 +1,158 @@ +; RUN: llvm-as < %s | llc -march=x86-64 | FileCheck %s --check-prefix=CHECK-64 +; RUN: llvm-as < %s | llc -march=x86 | FileCheck %s --check-prefix=CHECK-32 + +; CHECK-64: g64xh: +; CHECK-64: testb $8, %ah +; CHECK-64: ret +; CHECK-32: g64xh: +; CHECK-32: testb $8, %ah +; CHECK-32: ret +define void @g64xh(i64 inreg %x) nounwind { + %t = and i64 %x, 2048 + %s = icmp eq i64 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g64xl: +; CHECK-64: testb $8, %dil +; CHECK-64: ret +; CHECK-32: g64xl: +; CHECK-32: testb $8, %al +; CHECK-32: ret +define void @g64xl(i64 inreg %x) nounwind { + %t = and i64 %x, 8 + %s = icmp eq i64 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g32xh: +; CHECK-64: testb $8, %ah +; CHECK-64: ret +; CHECK-32: g32xh: +; CHECK-32: testb $8, %ah +; CHECK-32: ret +define void @g32xh(i32 inreg %x) nounwind { + %t = and i32 %x, 2048 + %s = icmp eq i32 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g32xl: +; CHECK-64: testb $8, %dil +; CHECK-64: ret +; CHECK-32: g32xl: +; CHECK-32: testb $8, %al +; CHECK-32: ret +define void @g32xl(i32 inreg %x) nounwind { + %t = and i32 %x, 8 + %s = icmp eq i32 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g16xh: +; CHECK-64: testb $8, %ah +; CHECK-64: ret +; CHECK-32: g16xh: +; CHECK-32: testb $8, %ah +; CHECK-32: ret +define void @g16xh(i16 inreg %x) nounwind { + %t = and i16 %x, 2048 + %s = icmp eq i16 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g16xl: +; CHECK-64: testb $8, %dil +; CHECK-64: ret +; CHECK-32: g16xl: +; CHECK-32: testb $8, %al +; CHECK-32: ret +define void @g16xl(i16 inreg %x) nounwind { + %t = and i16 %x, 8 + %s = icmp eq i16 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g64x16: +; CHECK-64: testw $32896, %di +; CHECK-64: ret +; CHECK-32: g64x16: +; CHECK-32: testw $32896, %ax +; CHECK-32: ret +define void @g64x16(i64 inreg %x) nounwind { + %t = and i64 %x, 32896 + %s = icmp eq i64 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g32x16: +; CHECK-64: testw $32896, %di +; CHECK-64: ret +; CHECK-32: g32x16: +; CHECK-32: testw $32896, %ax +; CHECK-32: ret +define void @g32x16(i32 inreg %x) nounwind { + %t = and i32 %x, 32896 + %s = icmp eq i32 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} +; CHECK-64: g64x32: +; CHECK-64: testl $268468352, %edi +; CHECK-64: ret +; CHECK-32: g64x32: +; CHECK-32: testl $268468352, %eax +; CHECK-32: ret +define void @g64x32(i64 inreg %x) nounwind { + %t = and i64 %x, 268468352 + %s = icmp eq i64 %t, 0 + br i1 %s, label %yes, label %no + +yes: + call void @bar() + ret void +no: + ret void +} + +declare void @bar()