[InstCombine] Canonicalize zext+overflow check to overflow check if zext's only purpose is to check overflow - #220658
Merged
Merged
Conversation
Contributor
Author
|
@Andarwinux Thoughts on this? |
…ext's only purpose is to check overflow Change processUMulZExtIdiom to also support adds, since the idiom is the same, except with add instead of mul. Alive2: https://alive2.llvm.org/ce/z/SsB4AK
|
@llvm/pr-subscribers-llvm-transforms Author: AZero13 (AZero13) ChangesChange processUMulZExtIdiom to also support adds, since the idiom is the same, except with add instead of mul. Alive2: https://alive2.llvm.org/ce/z/SsB4AK Full diff: https://github.com/llvm/llvm-project/pull/220658.diff 4 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 72f08b398e45d..ef6c74b49f7d5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -6777,72 +6777,76 @@ bool InstCombinerImpl::OptimizeOverflowCheck(Instruction::BinaryOps BinaryOp,
llvm_unreachable("Unexpected overflow result");
}
-/// Recognize and process idiom involving test for multiplication
+/// Recognize and process idiom involving test for unsigned
/// overflow.
///
/// The caller has matched a pattern of the form:
+/// I = cmp u (add(zext A, zext B), V
/// I = cmp u (mul(zext A, zext B), V
/// The function checks if this is a test for overflow and if so replaces
-/// multiplication with call to 'mul.with.overflow' intrinsic.
+/// addition/multiplication with call to the umul intrinsic or the canonical
+/// form of uadd overflow.
///
/// \param I Compare instruction.
-/// \param MulVal Result of 'mult' instruction. It is one of the arguments of
-/// the compare instruction. Must be of integer type.
+/// \param Val Result of add/mul instruction. It is one of the arguments of
+/// the compare instruction. Must be of integer type.
/// \param OtherVal The other argument of compare instruction.
/// \returns Instruction which must replace the compare instruction, NULL if no
/// replacement required.
-static Instruction *processUMulZExtIdiom(ICmpInst &I, Value *MulVal,
- const APInt *OtherVal,
- InstCombinerImpl &IC) {
+static Instruction *processUZExtIdiom(ICmpInst &I, Value *Val,
+ const APInt *OtherVal,
+ InstCombinerImpl &IC) {
// Don't bother doing this transformation for pointers, don't do it for
// vectors.
- if (!isa<IntegerType>(MulVal->getType()))
+ if (!isa<IntegerType>(Val->getType()))
return nullptr;
- auto *MulInstr = dyn_cast<Instruction>(MulVal);
- if (!MulInstr)
+ auto *Instr = dyn_cast<Instruction>(Val);
+ if (!Instr)
return nullptr;
- assert(MulInstr->getOpcode() == Instruction::Mul);
- auto *LHS = cast<ZExtInst>(MulInstr->getOperand(0)),
- *RHS = cast<ZExtInst>(MulInstr->getOperand(1));
+ unsigned Opcode = Instr->getOpcode();
+ assert(Opcode == Instruction::Add || Opcode == Instruction::Mul);
+
+ auto *LHS = cast<ZExtInst>(Instr->getOperand(0)),
+ *RHS = cast<ZExtInst>(Instr->getOperand(1));
assert(LHS->getOpcode() == Instruction::ZExt);
assert(RHS->getOpcode() == Instruction::ZExt);
Value *A = LHS->getOperand(0), *B = RHS->getOperand(0);
- // Calculate type and width of the result produced by mul.with.overflow.
+ // Calculate type and width of the result produced by add/mul.with.overflow.
Type *TyA = A->getType(), *TyB = B->getType();
unsigned WidthA = TyA->getPrimitiveSizeInBits(),
WidthB = TyB->getPrimitiveSizeInBits();
- unsigned MulWidth;
- Type *MulType;
+ unsigned ResultWidth;
+ Type *ResultType;
if (WidthB > WidthA) {
- MulWidth = WidthB;
- MulType = TyB;
+ ResultWidth = WidthB;
+ ResultType = TyB;
} else {
- MulWidth = WidthA;
- MulType = TyA;
+ ResultWidth = WidthA;
+ ResultType = TyA;
}
- // In order to replace the original mul with a narrower mul.with.overflow,
- // all uses must ignore upper bits of the product. The number of used low
- // bits must be not greater than the width of mul.with.overflow.
- if (MulVal->hasNUsesOrMore(2))
- for (User *U : MulVal->users()) {
+ // In order to replace the original result with a narrower one, all uses must
+ // ignore upper bits of the result. The number of used low bits must be not
+ // greater than the width of add or mul.with.overflow.
+ if (Val->hasNUsesOrMore(2))
+ for (User *U : Val->users()) {
if (U == &I)
continue;
if (TruncInst *TI = dyn_cast<TruncInst>(U)) {
- // Check if truncation ignores bits above MulWidth.
+ // Check if truncation ignores bits above ResultWidth.
unsigned TruncWidth = TI->getType()->getPrimitiveSizeInBits();
- if (TruncWidth > MulWidth)
+ if (TruncWidth > ResultWidth)
return nullptr;
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
- // Check if AND ignores bits above MulWidth.
+ // Check if AND ignores bits above ResultWidth.
if (BO->getOpcode() != Instruction::And)
return nullptr;
if (ConstantInt *CI = dyn_cast<ConstantInt>(BO->getOperand(1))) {
const APInt &CVal = CI->getValue();
- if (CVal.getBitWidth() - CVal.countl_zero() > MulWidth)
+ if (CVal.getBitWidth() - CVal.countl_zero() > ResultWidth)
return nullptr;
} else {
// In this case we could have the operand of the binary operation
@@ -6860,9 +6864,9 @@ static Instruction *processUMulZExtIdiom(ICmpInst &I, Value *MulVal,
switch (I.getPredicate()) {
case ICmpInst::ICMP_UGT: {
// Recognize pattern:
- // mulval = mul(zext A, zext B)
- // cmp ugt mulval, max
- APInt MaxVal = APInt::getMaxValue(MulWidth);
+ // val = add/mul(zext A, zext B)
+ // cmp ugt val, max
+ APInt MaxVal = APInt::getMaxValue(ResultWidth);
MaxVal = MaxVal.zext(OtherVal->getBitWidth());
if (MaxVal.eq(*OtherVal))
break; // Recognized
@@ -6871,9 +6875,9 @@ static Instruction *processUMulZExtIdiom(ICmpInst &I, Value *MulVal,
case ICmpInst::ICMP_ULT: {
// Recognize pattern:
- // mulval = mul(zext A, zext B)
- // cmp ule mulval, max + 1
- APInt MaxVal = APInt::getOneBitSet(OtherVal->getBitWidth(), MulWidth);
+ // val = add/mul(zext A, zext B)
+ // cmp ult val, max + 1
+ APInt MaxVal = APInt::getOneBitSet(OtherVal->getBitWidth(), ResultWidth);
if (MaxVal.eq(*OtherVal))
break; // Recognized
return nullptr;
@@ -6884,38 +6888,57 @@ static Instruction *processUMulZExtIdiom(ICmpInst &I, Value *MulVal,
}
InstCombiner::BuilderTy &Builder = IC.Builder;
- Builder.SetInsertPoint(MulInstr);
-
- // Replace: mul(zext A, zext B) --> mul.with.overflow(A, B)
- Value *MulA = A, *MulB = B;
- if (WidthA < MulWidth)
- MulA = Builder.CreateZExt(A, MulType);
- if (WidthB < MulWidth)
- MulB = Builder.CreateZExt(B, MulType);
- Value *Call =
- Builder.CreateIntrinsic(Intrinsic::umul_with_overflow, MulType,
- {MulA, MulB}, /*FMFSource=*/nullptr, "umul");
- IC.addToWorklist(MulInstr);
-
- // If there are uses of mul result other than the comparison, we know that
- // they are truncation or binary AND. Change them to use result of
- // mul.with.overflow and adjust properly mask/size.
- if (MulVal->hasNUsesOrMore(2)) {
- Value *Mul = Builder.CreateExtractValue(Call, 0, "umul.value");
- for (User *U : make_early_inc_range(MulVal->users())) {
+ Builder.SetInsertPoint(Instr);
+
+ // Replace: add/mul(zext A, zext B) --> canonical add/mul + overflow check
+ Value *ResultA = A, *ResultB = B;
+ if (WidthA < ResultWidth)
+ ResultA = Builder.CreateZExt(A, ResultType);
+ if (WidthB < ResultWidth)
+ ResultB = Builder.CreateZExt(B, ResultType);
+
+ Value *ArithResult;
+ Value *OverflowCheck;
+
+ if (Opcode == Instruction::Add) {
+ // Canonical add overflow check: add + compare
+ ArithResult = Builder.CreateAdd(ResultA, ResultB, "add");
+ // Overflow if result < either operand (for unsigned add)
+ if (I.getPredicate() == ICmpInst::ICMP_ULT)
+ OverflowCheck =
+ Builder.CreateICmpUGE(ArithResult, ResultA, "not.add.overflow");
+ else
+ OverflowCheck =
+ Builder.CreateICmpULT(ArithResult, ResultA, "add.overflow");
+ } else {
+ // For multiplication, the intrinsic is actually the canonical form
+ Value *Call = Builder.CreateIntrinsic(Intrinsic::umul_with_overflow,
+ ResultType, {ResultA, ResultB},
+ /*FMFSource=*/nullptr, "umul");
+ ArithResult = Builder.CreateExtractValue(Call, 0, "umul.value");
+ OverflowCheck = Builder.CreateExtractValue(Call, 1, "umul.overflow");
+ if (I.getPredicate() == ICmpInst::ICMP_ULT)
+ OverflowCheck = Builder.CreateNot(OverflowCheck);
+ }
+
+ IC.addToWorklist(Instr);
+
+ // Replace uses of the original add/mul result with the new arithmetic result
+ if (Val->hasNUsesOrMore(2)) {
+ for (User *U : make_early_inc_range(Val->users())) {
if (U == &I)
continue;
if (TruncInst *TI = dyn_cast<TruncInst>(U)) {
- if (TI->getType()->getPrimitiveSizeInBits() == MulWidth)
- IC.replaceInstUsesWith(*TI, Mul);
+ if (TI->getType()->getPrimitiveSizeInBits() == ResultWidth)
+ IC.replaceInstUsesWith(*TI, ArithResult);
else
- TI->setOperand(0, Mul);
+ TI->setOperand(0, ArithResult);
} else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(U)) {
assert(BO->getOpcode() == Instruction::And);
- // Replace (mul & mask) --> zext (mul.with.overflow & short_mask)
+ // Replace (ArithResult & mask) --> zext (ArithResult & short_mask)
ConstantInt *CI = cast<ConstantInt>(BO->getOperand(1));
- APInt ShortMask = CI->getValue().trunc(MulWidth);
- Value *ShortAnd = Builder.CreateAnd(Mul, ShortMask);
+ APInt ShortMask = CI->getValue().trunc(ResultWidth);
+ Value *ShortAnd = Builder.CreateAnd(ArithResult, ShortMask);
Value *Zext = Builder.CreateZExt(ShortAnd, BO->getType());
IC.replaceInstUsesWith(*BO, Zext);
} else {
@@ -6925,14 +6948,7 @@ static Instruction *processUMulZExtIdiom(ICmpInst &I, Value *MulVal,
}
}
- // The original icmp gets replaced with the overflow value, maybe inverted
- // depending on predicate.
- if (I.getPredicate() == ICmpInst::ICMP_ULT) {
- Value *Res = Builder.CreateExtractValue(Call, 1);
- return BinaryOperator::CreateNot(Res);
- }
-
- return ExtractValueInst::Create(Call, 1);
+ return IC.replaceInstUsesWith(I, OverflowCheck);
}
/// When performing a comparison against a constant, it is possible that not all
@@ -8207,10 +8223,12 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
}
}
+ // (zext X) + (zext Y) --> add + overflow check.
// (zext X) * (zext Y) --> llvm.umul.with.overflow.
- if (match(Op0, m_NUWMul(m_ZExt(m_Value(X)), m_ZExt(m_Value(Y)))) &&
+ if ((match(Op0, m_NUWAdd(m_ZExt(m_Value(X)), m_ZExt(m_Value(Y)))) ||
+ match(Op0, m_NUWMul(m_ZExt(m_Value(X)), m_ZExt(m_Value(Y))))) &&
match(Op1, m_APInt(C))) {
- if (Instruction *R = processUMulZExtIdiom(I, Op0, C, *this))
+ if (Instruction *R = processUZExtIdiom(I, Op0, C, *this))
return R;
}
diff --git a/llvm/test/Transforms/InstCombine/overflow-mul.ll b/llvm/test/Transforms/InstCombine/overflow-mul.ll
index 1d18d9ffd46d2..4aa59ac755e8d 100644
--- a/llvm/test/Transforms/InstCombine/overflow-mul.ll
+++ b/llvm/test/Transforms/InstCombine/overflow-mul.ll
@@ -286,8 +286,8 @@ define i32 @extra_and_use(i32 %x, i32 %y) {
; CHECK-LABEL: @extra_and_use(
; CHECK-NEXT: [[UMUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT: [[UMUL_VALUE:%.*]] = extractvalue { i32, i1 } [[UMUL]], 0
-; CHECK-NEXT: [[AND:%.*]] = zext i32 [[UMUL_VALUE]] to i64
; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[UMUL]], 1
+; CHECK-NEXT: [[AND:%.*]] = zext i32 [[UMUL_VALUE]] to i64
; CHECK-NEXT: call void @use.i64(i64 [[AND]])
; CHECK-NEXT: [[RETVAL:%.*]] = zext i1 [[OVERFLOW]] to i32
; CHECK-NEXT: ret i32 [[RETVAL]]
@@ -306,9 +306,9 @@ define i32 @extra_and_use_small_mask(i32 %x, i32 %y) {
; CHECK-LABEL: @extra_and_use_small_mask(
; CHECK-NEXT: [[UMUL:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
; CHECK-NEXT: [[UMUL_VALUE:%.*]] = extractvalue { i32, i1 } [[UMUL]], 0
+; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[UMUL]], 1
; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[UMUL_VALUE]], 268435455
; CHECK-NEXT: [[AND:%.*]] = zext nneg i32 [[TMP1]] to i64
-; CHECK-NEXT: [[OVERFLOW:%.*]] = extractvalue { i32, i1 } [[UMUL]], 1
; CHECK-NEXT: call void @use.i64(i64 [[AND]])
; CHECK-NEXT: [[RETVAL:%.*]] = zext i1 [[OVERFLOW]] to i32
; CHECK-NEXT: ret i32 [[RETVAL]]
diff --git a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
index 341459ed94c9d..608c5df739113 100644
--- a/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
+++ b/llvm/test/Transforms/InstCombine/saturating-add-sub.ll
@@ -2436,6 +2436,59 @@ define i8 @fold_add_umax_to_usub_multiuse(i8 %a) {
ret i8 %sel
}
+define i32 @uadd_with_zext(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext(
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X:%.*]], i32 [[Y:%.*]])
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ %cmp = icmp ugt i64 %add, 4294967295
+ %conv4 = trunc i64 %add to i32
+ %cond = select i1 %cmp, i32 -1, i32 %conv4
+ ret i32 %cond
+}
+
+define i32 @uadd_with_zext_multi_use(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext_multi_use(
+; CHECK-NEXT: [[TRUNCADD:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: call void @usei32(i32 [[TRUNCADD]])
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ %truncAdd = trunc i64 %add to i32
+ call void @usei32(i32 %truncAdd)
+ %cmp = icmp ugt i64 %add, 4294967295
+ %cond = select i1 %cmp, i32 -1, i32 %truncAdd
+ ret i32 %cond
+}
+
+define i32 @uadd_with_zext_neg_use(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext_neg_use(
+; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[X:%.*]] to i64
+; CHECK-NEXT: [[CONV1:%.*]] = zext i32 [[Y:%.*]] to i64
+; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV]], [[CONV1]]
+; CHECK-NEXT: call void @usei64(i64 [[ADD]])
+; CHECK-NEXT: [[COND1:%.*]] = call i64 @llvm.umin.i64(i64 [[ADD]], i64 4294967295)
+; CHECK-NEXT: [[COND:%.*]] = trunc nuw i64 [[COND1]] to i32
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ call void @usei64(i64 %add)
+ %cmp = icmp ugt i64 %add, 4294967295
+ %conv4 = trunc i64 %add to i32
+ %cond = select i1 %cmp, i32 -1, i32 %conv4
+ ret i32 %cond
+}
+
+declare void @usei64(i64)
+declare void @usei32(i32)
declare void @usei8(i8)
define i8 @sadd_sat_uge_int_max(i8 %x, i8 %y) {
diff --git a/llvm/test/Transforms/InstCombine/uadd-with-overflow.ll b/llvm/test/Transforms/InstCombine/uadd-with-overflow.ll
index eb021a0fd2c89..a2d709ee17821 100644
--- a/llvm/test/Transforms/InstCombine/uadd-with-overflow.ll
+++ b/llvm/test/Transforms/InstCombine/uadd-with-overflow.ll
@@ -147,3 +147,74 @@ define { <2 x i32>, <2 x i1> } @fold_simple_splat_constant_with_or_fail(<2 x i32
%b = tail call { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32> %a, <2 x i32> <i32 30, i32 30>)
ret { <2 x i32>, <2 x i1> } %b
}
+
+define i32 @uadd_with_zext(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[Y:%.*]], [[TMP1]]
+; CHECK-NEXT: [[COND:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ %cmp = icmp ugt i64 %add, 4294967295
+ %cond = zext i1 %cmp to i32
+ ret i32 %cond
+}
+
+define i32 @uadd_with_zext_use_and(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext_use_and(
+; CHECK-NEXT: [[UADD_VALUE:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[UADD_VALUE]], [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[UADD_VALUE]], 65535
+; CHECK-NEXT: [[AND:%.*]] = zext nneg i32 [[TMP1]] to i64
+; CHECK-NEXT: call void @usei64(i64 [[AND]])
+; CHECK-NEXT: [[COND:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ %and = and i64 %add, 65535
+ call void @usei64(i64 %and)
+ %cmp = icmp ugt i64 %add, 4294967295
+ %cond = zext i1 %cmp to i32
+ ret i32 %cond
+}
+
+define i32 @uadd_with_zext_inverse(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext_inverse(
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[Y:%.*]], [[TMP1]]
+; CHECK-NEXT: [[COND:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ %cmp = icmp ule i64 %add, 4294967295
+ %cond = zext i1 %cmp to i32
+ ret i32 %cond
+}
+
+define i32 @uadd_with_zext_neg_use(i32 %x, i32 %y) {
+; CHECK-LABEL: @uadd_with_zext_neg_use(
+; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[X:%.*]] to i64
+; CHECK-NEXT: [[CONV1:%.*]] = zext i32 [[Y:%.*]] to i64
+; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i64 [[CONV]], [[CONV1]]
+; CHECK-NEXT: call void @usei64(i64 [[ADD]])
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[ADD]], 4294967295
+; CHECK-NEXT: [[COND:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %conv = zext i32 %x to i64
+ %conv1 = zext i32 %y to i64
+ %add = add i64 %conv, %conv1
+ call void @usei64(i64 %add)
+ %cmp = icmp ugt i64 %add, 4294967295
+ %cond = zext i1 %cmp to i32
+ ret i32 %cond
+}
+
+declare void @usei64(i64)
|
Contributor
Author
|
All negative deltas, yay! |
Contributor
Author
|
@dtcxzyw Can you please review please. |
dtcxzyw
approved these changes
Sep 6, 2026
Contributor
Author
|
Thank you. @dtcxzyw Applied your changes! |
dtcxzyw
enabled auto-merge (squash)
September 6, 2026 19:19
dtcxzyw
disabled auto-merge
September 6, 2026 20:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Change processUMulZExtIdiom to also support adds, since the idiom is the same, except with add instead of mul.
Alive2: https://alive2.llvm.org/ce/z/SsB4AK