diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 10f25fa6c835..bbe32aee10db 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -58631,6 +58631,28 @@ static SDValue combineX86AddSub(SDNode *N, SelectionDAG &DAG, } } + // Fold ADD(ADC(Y, C1, CF), C2) -> ADC(Y, C1 + C2, CF) and + // ADD(ADC(Y, 0, CF), X) -> ADC(Y, X, CF) + if (!IsSub && LHS.getOpcode() == X86ISD::ADC && LHS.hasOneUse() && + !needCarryOrOverflowFlag(SDValue(N, 1))) { + + auto *C1 = dyn_cast(LHS.getOperand(1)); + auto *C2 = dyn_cast(RHS); + + // Both are constants: fold C1 + C2 + if (C1 && C2) { + APInt Sum = C1->getAPIntValue() + C2->getAPIntValue(); + return DAG.getNode(X86ISD::ADC, DL, N->getVTList(), LHS.getOperand(0), + DAG.getConstant(Sum, DL, VT), LHS.getOperand(2)); + } + + // Case: ADC(Y, 0, CF) + X -> ADC(Y, X, CF) + if (C1 && C1->isZero()) { + return DAG.getNode(X86ISD::ADC, DL, N->getVTList(), LHS.getOperand(0), + RHS, LHS.getOperand(2)); + } + } + // TODO: Can we drop the ZeroSecondOpOnly limit? This is to guarantee that the // EFLAGS result doesn't change. return combineAddOrSubToADCOrSBB(IsSub, DL, VT, LHS, RHS, DAG, diff --git a/llvm/test/CodeGen/X86/combine-add.ll b/llvm/test/CodeGen/X86/combine-add.ll index 51a8bf5b4841..5efc8cd111d1 100644 --- a/llvm/test/CodeGen/X86/combine-add.ll +++ b/llvm/test/CodeGen/X86/combine-add.ll @@ -561,3 +561,82 @@ define i64 @add_notx_x(i64 %v0) nounwind { %y = add i64 %x, %v0 ret i64 %y } + +; Basic positive test +define i32 @add_adc_to_adc(i32 %0, i32 %1, i32 %2) nounwind { +; CHECK-LABEL: add_adc_to_adc: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: cmpl %esi, %edi +; CHECK-NEXT: adcl $42, %edx +; CHECK-NEXT: cmovsl %esi, %eax +; CHECK-NEXT: retq + %4 = icmp ult i32 %0, %1 + %5 = zext i1 %4 to i32 + %6 = add i32 %2, 42 + %7 = add i32 %6, %5 + %8 = icmp slt i32 %7, 0 + %9 = select i1 %8, i32 %1, i32 %0 + ret i32 %9 +} + +; positive test: nonconst +define i32 @add_adc_to_adc_nonconst(i32 %0, i32 %1, i32 %2, i32 %extra) nounwind { +; CHECK-LABEL: add_adc_to_adc_nonconst: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: cmpl %esi, %edi +; CHECK-NEXT: adcl %ecx, %edx +; CHECK-NEXT: cmovsl %esi, %eax +; CHECK-NEXT: retq + %c = icmp ult i32 %0, %1 + %carry = zext i1 %c to i32 + %adc = add i32 %2, %carry + %final = add i32 %adc, %extra + %neg = icmp slt i32 %final, 0 + %sel = select i1 %neg, i32 %1, i32 %0 + ret i32 %sel +} + +; Negative test: Carry or overflow flag is used +define i32 @add_adc_wrong_flags(i32 %0, i32 %1, i32 %2) nounwind { +; CHECK-LABEL: add_adc_wrong_flags: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %esi, %eax +; CHECK-NEXT: cmpl %esi, %edi +; CHECK-NEXT: adcl $10, %edx +; CHECK-NEXT: addl $32, %edx +; CHECK-NEXT: cmovbl %edi, %eax +; CHECK-NEXT: retq + %4 = icmp ult i32 %0, %1 + %5 = zext i1 %4 to i32 + %6 = add i32 %2, 10 + %7 = add i32 %6, %5 + %8 = tail call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %7, i32 32) + %9 = extractvalue { i32, i1 } %8, 1 + %10 = select i1 %9, i32 %0, i32 %1 + ret i32 %10 +} + +; Negative test: Multi-use +define i32 @add_adc_multi_use(i32 %0, i32 %1, i32 %2) nounwind { +; CHECK-LABEL: add_adc_multi_use: +; CHECK: # %bb.0: +; CHECK-NEXT: # kill: def $edx killed $edx def $rdx +; CHECK-NEXT: # kill: def $esi killed $esi def $rsi +; CHECK-NEXT: cmpl %esi, %edi +; CHECK-NEXT: adcl $0, %edx +; CHECK-NEXT: movl %edx, %eax +; CHECK-NEXT: addl $42, %eax +; CHECK-NEXT: cmovsl %edi, %esi +; CHECK-NEXT: leal (%rsi,%rdx), %eax +; CHECK-NEXT: retq + %4 = icmp ult i32 %0, %1 + %5 = zext i1 %4 to i32 + %6 = add i32 %2, %5 + %7 = add i32 %6, 42 + %8 = icmp slt i32 %7, 0 + %9 = select i1 %8, i32 %0, i32 %1 + %10 = add i32 %9, %6 + ret i32 %10 +}