Index: lib/Target/X86/X86ISelDAGToDAG.cpp =================================================================== --- lib/Target/X86/X86ISelDAGToDAG.cpp +++ lib/Target/X86/X86ISelDAGToDAG.cpp @@ -190,6 +190,7 @@ private: void Select(SDNode *N) override; + SDValue pruneForLEAExtraction(SDNode *N); bool foldOffsetIntoAddress(uint64_t Offset, X86ISelAddressMode &AM); bool matchLoadInAddress(LoadSDNode *N, X86ISelAddressMode &AM); bool matchWrapper(SDValue N, X86ISelAddressMode &AM); @@ -699,12 +700,59 @@ return false; } +// Perform DAG pruning to facilitate LEA selection. +SDValue X86DAGToDAGISel::pruneForLEAExtraction(SDNode *N) { + EVT VT = N->getValueType(0); + if (!VT.isScalarInteger()) + return SDValue(); + + auto IsValidConstantShift = [=](SDValue Shift) { + if (Shift.getOpcode() != ISD::SHL) + return false; + SDNode *ShiftVal = Shift.getOperand(1).getNode(); + if (isa(ShiftVal) && + (cast(ShiftVal)->getZExtValue() <= 3)) + return true; + return false; + }; + + SDLoc DL(N); + unsigned Opcode = N->getOpcode(); + if (Opcode == ISD::SUB) { + SDValue N0 = N->getOperand(0); + SDValue N1 = N->getOperand(1); + if (IsValidConstantShift(N0)) { + // Following transformation is being done + // %1 = SHL X , 2 -> %1 = SUB 0 , Y + // %2 = SUB %1 , Y %2 = SHL X , 2 + // %3 = ADD %2 , %1 + SDValue OpsSUB[] = {CurDAG->getConstant(0, DL, VT), N1}; + SDValue NewSub = CurDAG->getNode(ISD::SUB, DL, VT, OpsSUB); + SDValue OpsAdd[] = {N0, NewSub}; + return CurDAG->getNode(ISD::ADD, DL, VT, OpsAdd); + } else if (IsValidConstantShift(N1)) { + // Following transformation is being done + // %1 = SHL X , CONST -> %1 = SUB 0 , X + // %2 = SUB Y , %1 %2 = SHL %1 , CONST + // %3 = ADD Y , %2 + SDValue OpsSUB[] = {CurDAG->getConstant(0, DL, VT), N1.getOperand(0)}; + SDValue NewSub = CurDAG->getNode(ISD::SUB, DL, VT, OpsSUB); + SDValue OpsShift[] = {NewSub, N1.getOperand(1)}; + SDValue NewShift = CurDAG->getNode(ISD::SHL, DL, VT, OpsShift); + SDValue OpsAdd[] = {N0, NewShift}; + return CurDAG->getNode(ISD::ADD, DL, VT, OpsAdd); + } + } + return SDValue(); +} + void X86DAGToDAGISel::PreprocessISelDAG() { // OptFor[Min]Size are used in pattern predicates that isel is matching. OptForSize = MF->getFunction().optForSize(); OptForMinSize = MF->getFunction().optForMinSize(); assert((!OptForMinSize || OptForSize) && "OptForMinSize implies OptForSize"); + bool TriggerDeadNodesRemoval = false; for (SelectionDAG::allnodes_iterator I = CurDAG->allnodes_begin(), E = CurDAG->allnodes_end(); I != E; ) { SDNode *N = &*I++; // Preincrement iterator to avoid invalidation issues. @@ -721,6 +769,20 @@ continue; } + // Look for pattern which could be transformed to facilitate + // addressing mode based LEA selection. + if (OptForSize) { + SDValue NewN = pruneForLEAExtraction(N); + if (NewN.getNode()) { + --I; + CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), NewN); + ++I; + CurDAG->DeleteNode(N); + TriggerDeadNodesRemoval = true; + continue; + } + } + if (OptLevel != CodeGenOpt::None && // Only do this when the target can fold the load into the call or // jmp. @@ -825,6 +887,8 @@ ++I; CurDAG->DeleteNode(N); } + if (TriggerDeadNodesRemoval) + CurDAG->RemoveDeadNodes(); } Index: test/CodeGen/X86/lea-opt.ll =================================================================== --- test/CodeGen/X86/lea-opt.ll +++ test/CodeGen/X86/lea-opt.ll @@ -307,3 +307,162 @@ sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry ret void } + +define i32 @foo1(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %esi +; CHECK-NEXT: leal (%rdi,%rsi,2), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 %y, -2 + %add = add nsw i32 %mul, %x + ret i32 %add +} + +define i32 @foo2(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: leal (%rsi,%rsi,2), %eax +; CHECK-NEXT: subl %eax, %edi +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 %y, -3 + %add = add nsw i32 %mul, %x + ret i32 %add +} + +define i32 @foo3(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo3: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %esi +; CHECK-NEXT: leal (%rdi,%rsi,4), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 %y, -4 + %add = add nsw i32 %mul, %x + ret i32 %add +} + +define i32 @foo4(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo4: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %edi +; CHECK-NEXT: leal (%rdi,%rsi,4), %eax +; CHECK-NEXT: retq +entry: + %mul = shl nsw i32 %y, 2 + %sub = sub nsw i32 %mul, %x + ret i32 %sub +} + + +define i32 @foo5(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo5: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %esi +; CHECK-NEXT: leal (%rdi,%rsi,2), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 -2, %y + %add = add nsw i32 %x, %mul + ret i32 %add +} + +define i32 @foo6(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo6: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: leal (%rsi,%rsi,2), %eax +; CHECK-NEXT: subl %eax, %edi +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 -3, %y + %add = add nsw i32 %x, %mul + ret i32 %add +} + +define i32 @foo7(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo7: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %esi +; CHECK-NEXT: leal (%rdi,%rsi,4), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 -4, %y + %add = add nsw i32 %x, %mul + ret i32 %add +} + +define i32 @foo8(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo8: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %edi +; CHECK-NEXT: leal (%rdi,%rsi,4), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 4, %y + %sub = sub nsw i32 %mul, %x + ret i32 %sub +} + +define i64 @foo9(i64 %x, i64 %y) #0 { +; CHECK-LABEL: foo9: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: negq %rsi +; CHECK-NEXT: leaq (%rdi,%rsi,4), %rax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i64 -4, %y + %add = add nsw i64 %x, %mul + ret i64 %add +} + +define i32 @foo10(i32 %x, i32 %y) #0 { +; CHECK-LABEL: foo10: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %edi +; CHECK-NEXT: leal (%rdi,%rsi,4), %eax +; CHECK-NEXT: retq +entry: + %mul = mul nsw i32 4, %y + %sub = sub nsw i32 %mul, %x + ret i32 %sub +} + +define zeroext i16 @foo11(i16 zeroext %x, i16 zeroext %y) #0 { +; CHECK-LABEL: foo11: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: # kill: def $edi killed $edi def $rdi +; CHECK-NEXT: negl %esi +; CHECK-NEXT: leal (%rdi,%rsi,8), %eax +; CHECK-NEXT: # kill: def $ax killed $ax killed $eax +; CHECK-NEXT: retq +entry: + %conv = zext i16 %x to i32 + %conv1 = zext i16 %y to i32 + %mul = mul nsw i32 -8, %conv1 + %add = add nsw i32 %conv, %mul + %conv2 = trunc i32 %add to i16 + ret i16 %conv2 +} + +attributes #0 = { norecurse nounwind optsize readnone uwtable}