[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