[llvm] [EarlyCSE] CSE with.overflow result extracts with binops (PR #198992)

Jiang Ning via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 02:45:11 PDT 2026


https://github.com/JiangNingHX updated https://github.com/llvm/llvm-project/pull/198992

>From 8fd1556096d001ec230bb58e77b5081832de0398 Mon Sep 17 00:00:00 2001
From: JiangNing <jiangninghx at foxmail.com>
Date: Fri, 22 May 2026 14:07:01 +0800
Subject: [PATCH] [EarlyCSE] CSE with.overflow result extracts with binops

Teach EarlyCSE value numbering that index 0 of integer with.overflow
intrinsics is equivalent to the corresponding wrapping add/sub/mul
expression when a matching value is already available.

Do not apply the cross-kind equivalence to no-wrap binary operators,
because those may produce poison while with.overflow returns the
wrapped arithmetic result.
---
 llvm/lib/Transforms/Scalar/EarlyCSE.cpp       |  53 +++
 .../EarlyCSE/with-overflow-recompute.ll       | 420 ++++++++++++++++++
 2 files changed, 473 insertions(+)
 create mode 100644 llvm/test/Transforms/EarlyCSE/with-overflow-recompute.ll

diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index 3dd84bddb6bf5..7e70e9850823d 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -213,8 +213,42 @@ static unsigned hashCallInst(CallInst *CI) {
                       hash_combine_range(CI->operand_values()));
 }
 
+static bool matchOverflowResultOrBinaryOp(Instruction *Inst, unsigned &Opcode,
+                                          Value *&LHS, Value *&RHS,
+                                          BinaryOperator *&MatchedBinOp) {
+  MatchedBinOp = nullptr;
+  if (auto *BinOp = dyn_cast<BinaryOperator>(Inst)) {
+    Opcode = BinOp->getOpcode();
+    if (Opcode != Instruction::Add && Opcode != Instruction::Sub &&
+        Opcode != Instruction::Mul)
+      return false;
+    MatchedBinOp = BinOp;
+    LHS = BinOp->getOperand(0);
+    RHS = BinOp->getOperand(1);
+  } else if (auto *EVI = dyn_cast<ExtractValueInst>(Inst)) {
+    auto *WO = dyn_cast<WithOverflowInst>(EVI->getAggregateOperand());
+    if (!WO || EVI->getNumIndices() != 1 || *EVI->idx_begin() != 0)
+      return false;
+    Opcode = WO->getBinaryOp();
+    LHS = WO->getLHS();
+    RHS = WO->getRHS();
+  } else {
+    return false;
+  }
+
+  if (Instruction::isCommutative(Opcode) && LHS > RHS)
+    std::swap(LHS, RHS);
+  return true;
+}
+
 static unsigned getHashValueImpl(SimpleValue Val) {
   Instruction *Inst = Val.Inst;
+  unsigned Opcode;
+  Value *LHS, *RHS;
+  BinaryOperator *BinOp;
+  if (matchOverflowResultOrBinaryOp(Inst, Opcode, LHS, RHS, BinOp))
+    return hash_combine(Opcode, LHS, RHS);
+
   // Hash in all of the operands as pointers.
   if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Inst)) {
     Value *LHS = BinOp->getOperand(0);
@@ -337,6 +371,25 @@ unsigned DenseMapInfo<SimpleValue>::getHashValue(SimpleValue Val) {
 static bool isEqualImpl(SimpleValue LHS, SimpleValue RHS) {
   Instruction *LHSI = LHS.Inst, *RHSI = RHS.Inst;
 
+  unsigned LOpcode, ROpcode;
+  Value *LLHS, *LRHS, *RLHS, *RRHS;
+  BinaryOperator *LBinOp, *RBinOp;
+  bool LMatched =
+      matchOverflowResultOrBinaryOp(LHSI, LOpcode, LLHS, LRHS, LBinOp);
+  bool RMatched =
+      matchOverflowResultOrBinaryOp(RHSI, ROpcode, RLHS, RRHS, RBinOp);
+  if (LMatched && RMatched && (!LBinOp || !RBinOp)) {
+    // The overflow intrinsics return a wrapped result, so do not replace one
+    // with a no-wrap binary operator that may produce poison.
+    if (LBinOp && LBinOp->hasPoisonGeneratingFlags())
+      return false;
+    if (RBinOp && RBinOp->hasPoisonGeneratingFlags())
+      return false;
+    return LOpcode == ROpcode && LLHS == RLHS && LRHS == RRHS;
+  }
+  if (LMatched != RMatched)
+    return false;
+
   if (LHSI->getOpcode() != RHSI->getOpcode())
     return false;
   if (LHSI->isIdenticalToWhenDefined(RHSI, /*IntersectAttrs=*/true)) {
diff --git a/llvm/test/Transforms/EarlyCSE/with-overflow-recompute.ll b/llvm/test/Transforms/EarlyCSE/with-overflow-recompute.ll
new file mode 100644
index 0000000000000..a3dea03152a67
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/with-overflow-recompute.ll
@@ -0,0 +1,420 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=early-cse -earlycse-debug-hash -S | FileCheck %s
+; RUN: opt < %s -passes='early-cse<memssa>' -S | FileCheck %s
+
+define i32 @uadd_extract_then_add(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @uadd_extract_then_add(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  %recompute = add i32 %x, %y
+  %r = or i32 %sum, %recompute
+  ret i32 %r
+}
+
+define i32 @sadd_extract_then_add(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sadd_extract_then_add(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+  %wo = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  %recompute = add i32 %x, %y
+  %r = or i32 %sum, %recompute
+  ret i32 %r
+}
+
+define i32 @usub_extract_then_sub(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @usub_extract_then_sub(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[DIFF:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[DIFF]]
+;
+  %wo = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %x, i32 %y)
+  %diff = extractvalue { i32, i1 } %wo, 0
+  %recompute = sub i32 %x, %y
+  %r = or i32 %diff, %recompute
+  ret i32 %r
+}
+
+define i32 @ssub_extract_then_sub(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @ssub_extract_then_sub(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[DIFF:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[DIFF]]
+;
+  %wo = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)
+  %diff = extractvalue { i32, i1 } %wo, 0
+  %recompute = sub i32 %x, %y
+  %r = or i32 %diff, %recompute
+  ret i32 %r
+}
+
+define i32 @umul_extract_then_mul(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @umul_extract_then_mul(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[PRODUCT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[PRODUCT]]
+;
+  %wo = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %x, i32 %y)
+  %product = extractvalue { i32, i1 } %wo, 0
+  %recompute = mul i32 %x, %y
+  %r = or i32 %product, %recompute
+  ret i32 %r
+}
+
+define i32 @smul_extract_then_mul(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @smul_extract_then_mul(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[PRODUCT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[PRODUCT]]
+;
+  %wo = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %x, i32 %y)
+  %product = extractvalue { i32, i1 } %wo, 0
+  %recompute = mul i32 %x, %y
+  %r = or i32 %product, %recompute
+  ret i32 %r
+}
+
+define i32 @add_then_uadd_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @add_then_uadd_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[WO]], 1
+; CHECK-NEXT:    [[OVERFLOW_EXT:%.*]] = zext i1 [[OVERFLOW]] to i32
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[SUM]], [[OVERFLOW_EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %sum = add i32 %x, %y
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %overflow = extractvalue { i32, i1 } %wo, 1
+  %overflow.ext = zext i1 %overflow to i32
+  %same = or i32 %sum, %result
+  %r = add i32 %same, %overflow.ext
+  ret i32 %r
+}
+
+define i32 @sub_then_usub_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sub_then_usub_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIFF:%.*]] = sub i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[WO]], 1
+; CHECK-NEXT:    [[OVERFLOW_EXT:%.*]] = zext i1 [[OVERFLOW]] to i32
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[DIFF]], [[OVERFLOW_EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %diff = sub i32 %x, %y
+  %wo = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %overflow = extractvalue { i32, i1 } %wo, 1
+  %overflow.ext = zext i1 %overflow to i32
+  %same = or i32 %diff, %result
+  %r = add i32 %same, %overflow.ext
+  ret i32 %r
+}
+
+define i32 @mul_then_umul_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @mul_then_umul_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[PRODUCT:%.*]] = mul i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[WO]], 1
+; CHECK-NEXT:    [[OVERFLOW_EXT:%.*]] = zext i1 [[OVERFLOW]] to i32
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[PRODUCT]], [[OVERFLOW_EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %product = mul i32 %x, %y
+  %wo = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %overflow = extractvalue { i32, i1 } %wo, 1
+  %overflow.ext = zext i1 %overflow to i32
+  %same = or i32 %product, %result
+  %r = add i32 %same, %overflow.ext
+  ret i32 %r
+}
+
+define i32 @commuted_add_extract_then_add(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @commuted_add_extract_then_add(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[SUM]]
+;
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  %recompute = add i32 %y, %x
+  %r = or i32 %sum, %recompute
+  ret i32 %r
+}
+
+define i32 @commuted_mul_extract_then_mul(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @commuted_mul_extract_then_mul(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[PRODUCT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i32 [[PRODUCT]]
+;
+  %wo = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %x, i32 %y)
+  %product = extractvalue { i32, i1 } %wo, 0
+  %recompute = mul i32 %y, %x
+  %r = or i32 %product, %recompute
+  ret i32 %r
+}
+
+define i32 @reversed_sub_not_cse(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @reversed_sub_not_cse(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[DIFF:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[RECOMPUTE:%.*]] = sub i32 [[Y]], [[X]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[DIFF]], [[RECOMPUTE]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %wo = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %x, i32 %y)
+  %diff = extractvalue { i32, i1 } %wo, 0
+  %recompute = sub i32 %y, %x
+  %r = add i32 %diff, %recompute
+  ret i32 %r
+}
+
+define i32 @signed_unsigned_add_extracts(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @signed_unsigned_add_extracts(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[U:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[URESULT:%.*]] = extractvalue { i32, i1 } [[U]], 0
+; CHECK-NEXT:    [[S:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SOVERFLOW:%.*]] = extractvalue { i32, i1 } [[S]], 1
+; CHECK-NEXT:    [[SOVERFLOW_EXT:%.*]] = zext i1 [[SOVERFLOW]] to i32
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[URESULT]], [[SOVERFLOW_EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %u = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %uresult = extractvalue { i32, i1 } %u, 0
+  %s = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)
+  %sresult = extractvalue { i32, i1 } %s, 0
+  %soverflow = extractvalue { i32, i1 } %s, 1
+  %soverflow.ext = zext i1 %soverflow to i32
+  %same = or i32 %uresult, %sresult
+  %r = add i32 %same, %soverflow.ext
+  ret i32 %r
+}
+
+define <2 x i32> @vector_uadd_extract_then_add(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i32> @vector_uadd_extract_then_add(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32> [[X]], <2 x i32> [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { <2 x i32>, <2 x i1> } [[WO]], 0
+; CHECK-NEXT:    ret <2 x i32> [[SUM]]
+;
+  %wo = call { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32> %x, <2 x i32> %y)
+  %sum = extractvalue { <2 x i32>, <2 x i1> } %wo, 0
+  %recompute = add <2 x i32> %x, %y
+  %r = or <2 x i32> %sum, %recompute
+  ret <2 x i32> %r
+}
+
+define i32 @overflow_bit_not_arithmetic(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @overflow_bit_not_arithmetic(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[WO]], 1
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[OVERFLOW_EXT:%.*]] = zext i1 [[OVERFLOW]] to i32
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[SUM]], [[OVERFLOW_EXT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %overflow = extractvalue { i32, i1 } %wo, 1
+  %sum = add i32 %x, %y
+  %overflow.ext = zext i1 %overflow to i32
+  %r = add i32 %sum, %overflow.ext
+  ret i32 %r
+}
+
+define i32 @mismatched_opcode_not_cse(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @mismatched_opcode_not_cse(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[PRODUCT:%.*]] = mul i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[SUM]], [[PRODUCT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  %product = mul i32 %x, %y
+  %r = add i32 %sum, %product
+  ret i32 %r
+}
+
+define i32 @mismatched_operand_not_cse(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: define i32 @mismatched_operand_not_cse(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[OTHER:%.*]] = add i32 [[X]], [[Z]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[SUM]], [[OTHER]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  %other = add i32 %x, %z
+  %r = add i32 %sum, %other
+  ret i32 %r
+}
+
+define i64 @i64_uadd_extract_then_add(i64 %x, i64 %y) {
+; CHECK-LABEL: define i64 @i64_uadd_extract_then_add(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]]) {
+; CHECK-NEXT:    [[WO:%.*]] = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 [[X]], i64 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i64, i1 } [[WO]], 0
+; CHECK-NEXT:    ret i64 [[SUM]]
+;
+  %wo = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x, i64 %y)
+  %sum = extractvalue { i64, i1 } %wo, 0
+  %recompute = add i64 %x, %y
+  %r = or i64 %sum, %recompute
+  ret i64 %r
+}
+
+define i32 @nuw_add_leader_not_used_for_uadd_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @nuw_add_leader_not_used_for_uadd_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[SUM:%.*]] = add nuw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = freeze i32 [[SUM]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[RESULT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[FROZEN]], [[RESULT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %sum = add nuw i32 %x, %y
+  %frozen = freeze i32 %sum
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %r = add i32 %frozen, %result
+  ret i32 %r
+}
+
+define i32 @nsw_add_leader_not_used_for_sadd_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @nsw_add_leader_not_used_for_sadd_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[SUM:%.*]] = add nsw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = freeze i32 [[SUM]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[RESULT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[FROZEN]], [[RESULT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %sum = add nsw i32 %x, %y
+  %frozen = freeze i32 %sum
+  %wo = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %r = add i32 %frozen, %result
+  ret i32 %r
+}
+
+define i32 @nuw_sub_leader_not_used_for_usub_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @nuw_sub_leader_not_used_for_usub_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIFF:%.*]] = sub nuw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = freeze i32 [[DIFF]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[RESULT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[FROZEN]], [[RESULT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %diff = sub nuw i32 %x, %y
+  %frozen = freeze i32 %diff
+  %wo = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %r = add i32 %frozen, %result
+  ret i32 %r
+}
+
+define i32 @nsw_sub_leader_not_used_for_ssub_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @nsw_sub_leader_not_used_for_ssub_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[DIFF:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = freeze i32 [[DIFF]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[RESULT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[FROZEN]], [[RESULT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %diff = sub nsw i32 %x, %y
+  %frozen = freeze i32 %diff
+  %wo = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %r = add i32 %frozen, %result
+  ret i32 %r
+}
+
+define i32 @nuw_mul_leader_not_used_for_umul_extract(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @nuw_mul_leader_not_used_for_umul_extract(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[PRODUCT:%.*]] = mul nuw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = freeze i32 [[PRODUCT]]
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[RESULT:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[FROZEN]], [[RESULT]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+  %product = mul nuw i32 %x, %y
+  %frozen = freeze i32 %product
+  %wo = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 %x, i32 %y)
+  %result = extractvalue { i32, i1 } %wo, 0
+  %r = add i32 %frozen, %result
+  ret i32 %r
+}
+
+define i32 @non_dominating_extract_not_cse(i1 %c, i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @non_dominating_extract_not_cse(
+; CHECK-SAME: i1 [[C:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[MERGE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    [[WO:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    [[SUM:%.*]] = extractvalue { i32, i1 } [[WO]], 0
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    [[P:%.*]] = phi i32 [ [[SUM]], %[[THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    [[RECOMPUTE:%.*]] = add i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[R:%.*]] = add i32 [[P]], [[RECOMPUTE]]
+; CHECK-NEXT:    ret i32 [[R]]
+;
+entry:
+  br i1 %c, label %then, label %merge
+
+then:
+  %wo = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)
+  %sum = extractvalue { i32, i1 } %wo, 0
+  br label %merge
+
+merge:
+  %p = phi i32 [ %sum, %then ], [ 0, %entry ]
+  %recompute = add i32 %x, %y
+  %r = add i32 %p, %recompute
+  ret i32 %r
+}
+
+declare { i32, i1 } @llvm.uadd.with.overflow.i32(i32, i32)
+declare { i64, i1 } @llvm.uadd.with.overflow.i64(i64, i64)
+declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.usub.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.ssub.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.umul.with.overflow.i32(i32, i32)
+declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32)
+declare { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32>, <2 x i32>)



More information about the llvm-commits mailing list