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; + bool pruneForLEAExtraction(SDNode *N); bool foldOffsetIntoAddress(uint64_t Offset, X86ISelAddressMode &AM); bool matchLoadInAddress(LoadSDNode *N, X86ISelAddressMode &AM); bool matchWrapper(SDValue N, X86ISelAddressMode &AM); @@ -699,6 +700,57 @@ return false; } +// Perform DAG pruning to facilitate LEA selection. +bool X86DAGToDAGISel::pruneForLEAExtraction(SDNode *N) { + EVT VT = N->getValueType(0); + if (!VT.isScalarInteger()) + return false; + auto IsValidConstantShift = [=](SDValue Shift) { + if (Shift.getOpcode() != ISD::SHL) + return false; + if (!Shift.hasOneUse()) + return false; + SDNode *ShiftVal = Shift.getOperand(1).getNode(); + if (isa(ShiftVal) && + (cast(ShiftVal)->getZExtValue() <= 8)) + 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}; + SDValue NewAdd = CurDAG->getNode(ISD::ADD, DL, VT, OpsAdd); + CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), NewAdd); + return true; + } 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}; + SDValue NewAdd = CurDAG->getNode(ISD::ADD, DL, VT, OpsAdd); + CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 0), NewAdd); + return true; + } + } + return false; +} + void X86DAGToDAGISel::PreprocessISelDAG() { // OptFor[Min]Size are used in pattern predicates that isel is matching. OptForSize = MF->getFunction().optForSize(); @@ -721,6 +773,11 @@ continue; } + // Look for pattern which could be transformed to facilitate + // addressing mode based LEA selection. + if (OptForSize && pruneForLEAExtraction(N)) + continue; + if (OptLevel != CodeGenOpt::None && // Only do this when the target can fold the load into the call or // jmp. Index: test/CodeGen/X86/lea-opt.ll =================================================================== --- test/CodeGen/X86/lea-opt.ll +++ test/CodeGen/X86/lea-opt.ll @@ -307,3 +307,65 @@ sw.epilog: ; preds = %sw.bb.2, %sw.bb.1, %entry ret void } + +; Function Attrs: norecurse nounwind optsize readnone uwtable +define dso_local i32 @foo1(i32 %x, i32 %y) local_unnamed_addr #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 +} + +; Function Attrs: norecurse nounwind optsize readnone uwtable +define dso_local i32 @foo2(i32 %x, i32 %y) local_unnamed_addr #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 +} + +; Function Attrs: norecurse nounwind optsize readnone uwtable +define dso_local i32 @foo3(i32 %x, i32 %y) local_unnamed_addr #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 +} + +; Function Attrs: norecurse nounwind optsize readnone uwtable +define dso_local i32 @foo4(i32 %x, i32 %y) local_unnamed_addr #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 +} + +attributes #0 = { norecurse nounwind optsize readnone uwtable}