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 != MVT::i32 && VT != MVT::i64) + 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 @@ -311,9 +311,10 @@ define i32 @test5(i32 %x, i32 %y) #0 { ; CHECK-LABEL: test5: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: addl %esi, %esi -; CHECK-NEXT: subl %esi, %edi -; CHECK-NEXT: movl %edi, %eax +; 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 @@ -338,9 +339,10 @@ define i32 @test7(i32 %x, i32 %y) #0 { ; CHECK-LABEL: test7: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: shll $2, %esi -; CHECK-NEXT: subl %esi, %edi -; CHECK-NEXT: movl %edi, %eax +; 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 @@ -352,8 +354,9 @@ ; CHECK-LABEL: test8: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi -; CHECK-NEXT: leal (,%rsi,4), %eax -; CHECK-NEXT: subl %edi, %eax +; 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 @@ -361,13 +364,13 @@ ret i32 %sub } - define i32 @test9(i32 %x, i32 %y) #0 { ; CHECK-LABEL: test9: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: addl %esi, %esi -; CHECK-NEXT: subl %esi, %edi -; CHECK-NEXT: movl %edi, %eax +; 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 @@ -392,9 +395,10 @@ define i32 @test11(i32 %x, i32 %y) #0 { ; CHECK-LABEL: test11: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: shll $2, %esi -; CHECK-NEXT: subl %esi, %edi -; CHECK-NEXT: movl %edi, %eax +; 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 @@ -406,8 +410,9 @@ ; CHECK-LABEL: test12: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi -; CHECK-NEXT: leal (,%rsi,4), %eax -; CHECK-NEXT: subl %edi, %eax +; 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 @@ -418,9 +423,8 @@ define i64 @test13(i64 %x, i64 %y) #0 { ; CHECK-LABEL: test13: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: shlq $2, %rsi -; CHECK-NEXT: subq %rsi, %rdi -; CHECK-NEXT: movq %rdi, %rax +; CHECK-NEXT: negq %rsi +; CHECK-NEXT: leaq (%rdi,%rsi,4), %rax ; CHECK-NEXT: retq entry: %mul = mul nsw i64 -4, %y @@ -432,8 +436,9 @@ ; CHECK-LABEL: test14: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: # kill: def $esi killed $esi def $rsi -; CHECK-NEXT: leal (,%rsi,4), %eax -; CHECK-NEXT: subl %edi, %eax +; 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 @@ -444,9 +449,11 @@ define zeroext i16 @test15(i16 zeroext %x, i16 zeroext %y) #0 { ; CHECK-LABEL: test15: ; CHECK: # %bb.0: # %entry -; CHECK-NEXT: shll $3, %esi -; CHECK-NEXT: subl %esi, %edi -; CHECK-NEXT: movl %edi, %eax +; 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