Skip to content

Conversation

@Charukesh827
Copy link

@Charukesh827 Charukesh827 commented May 19, 2025

As suggested generalize to fold max(max(x, c1) binop c2, c3) —> max(x binop c2, c3) if c3>=C1* 2 ^ c2 is done.

define i8 @src(i8 %arg0) {
%1 = call i8 @llvm.umax.i8(i8 %arg0, i8 1)
%2 = shl nuw i8 %1, 1
%3 = call i8 @llvm.umax.i8(i8 %2, i8 16)
ret i8 %3
}

define i8 @tgt(i8 %arg0) {
%1 = shl nuw i8 %arg0, 1
%2 = call i8 @llvm.umax.i8(i8 %1, i8 16)
ret i8 %2
}

Closes #139786.

…ue Missed Optimization: max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2 llvm#139786
…ax(x << c2, c3) when c3 >= c1 * 2 ^ c2

This patch fixes issue llvm#139786
 where InstCombine where it Missed Optimization: max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2. Pre-committed test in <commit-hash>.

Alive2: https://alive2.llvm.org/ce/z/on2tJE
@Charukesh827 Charukesh827 requested a review from nikic as a code owner May 19, 2025 10:33
@github-actions
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels May 19, 2025
@llvmbot
Copy link
Member

llvmbot commented May 19, 2025

@llvm/pr-subscribers-llvm-transforms

Author: None (Charukesh827)

Changes

As suggested generalize to fold max(max(x, c1) binop c2, c3) —> max(x binop c2, c3) if c3>=C1* 2 ^ c2 is done.

define i8 @src(i8 %arg0) {
%1 = call i8 @llvm.umax.i8(i8 %arg0, i8 1)
%2 = shl nuw i8 %1, 1
%3 = call i8 @llvm.umax.i8(i8 %2, i8 16)
ret i8 %3
}

define i8 @tgt(i8 %arg0) {
%1 = shl nuw i8 %arg0, 1
%2 = call i8 @llvm.umax.i8(i8 %1, i8 16)
ret i8 %2
}


Full diff: https://github.com/llvm/llvm-project/pull/140526.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+83)
  • (added) llvm/test/Transforms/InstCombine/shift-binop.ll (+27)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 3d35bf753c40e..53dd5f803f97b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1171,6 +1171,84 @@ static Instruction *moveAddAfterMinMax(IntrinsicInst *II,
   return IsSigned ? BinaryOperator::CreateNSWAdd(NewMinMax, Add->getOperand(1))
                   : BinaryOperator::CreateNUWAdd(NewMinMax, Add->getOperand(1));
 }
+
+//Try canonicalize min/max(x << shamt, c<<shamt) into max(x, c) << shamt
+static Instruction *moveShiftAfterMinMax(IntrinsicInst *II, InstCombiner::BuilderTy &Builder) {
+  Intrinsic::ID MinMaxID = II->getIntrinsicID();
+  assert((MinMaxID == Intrinsic::smax || MinMaxID == Intrinsic::smin ||
+    MinMaxID == Intrinsic::umax || MinMaxID == Intrinsic::umin) &&
+   "Expected a min or max intrinsic");
+  
+  Value *Op0 = II->getArgOperand(0), *Op1 = II->getArgOperand(1);
+  Value *InnerMax;
+  const APInt *C;
+  if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_APInt(C)))) || 
+      !match(Op1, m_APInt(C)))
+      return nullptr;
+  
+  auto* BinOpInst = cast<BinaryOperator>(Op0);
+  Instruction::BinaryOps BinOp = BinOpInst->getOpcode();
+  Value *X;
+  InnerMax = BinOpInst->getOperand(0);
+  // std::cout<< InnerMax->dump() <<std::endl;
+  if(!match(InnerMax,m_OneUse(m_Intrinsic<Intrinsic::umax>(m_Value(X),m_APInt(C))))){
+  if(!match(InnerMax,m_OneUse(m_Intrinsic<Intrinsic::smax>(m_Value(X),m_APInt(C))))){
+  if(!match(InnerMax,m_OneUse(m_Intrinsic<Intrinsic::umin>(m_Value(X),m_APInt(C))))){
+  if(!match(InnerMax,m_OneUse(m_Intrinsic<Intrinsic::smin>(m_Value(X),m_APInt(C))))){
+     return nullptr;
+  }}}}
+  
+  auto *InnerMaxInst = cast<IntrinsicInst>(InnerMax);
+
+  bool IsSigned = MinMaxID == Intrinsic::smax || MinMaxID == Intrinsic::smin;
+  if((IsSigned && !BinOpInst->hasNoSignedWrap()) ||
+     (!IsSigned && !BinOpInst->hasNoUnsignedWrap())) 
+     return nullptr;
+
+  // Check if BinOp is a left shift
+  if (BinOp != Instruction::Shl) {
+    return nullptr;
+  }
+
+  APInt C2=llvm::dyn_cast<llvm::ConstantInt>(BinOpInst->getOperand(1))->getValue() ;
+  APInt C3=llvm::dyn_cast<llvm::ConstantInt>(II->getArgOperand(1))->getValue();
+  APInt C1=llvm::dyn_cast<llvm::ConstantInt>(InnerMaxInst->getOperand(1))->getValue();
+
+  // Compute C1 * 2^C2
+  APInt Two = APInt(C2.getBitWidth(), 2);
+  APInt Pow2C2 = Two.shl(C2); // 2^C2
+  APInt C1TimesPow2C2 = C1 * Pow2C2; // C1 * 2^C2
+
+  // Check C3 >= C1 * 2^C2
+  if (C3.ult(C1TimesPow2C2)) {
+    return nullptr;
+  }
+
+  //Create new x binop c2
+  Value *NewBinOp = Builder.CreateBinOp(BinOp, InnerMaxInst->getOperand(0), BinOpInst->getOperand(1) );
+  
+  //Create new min/max intrinsic with new binop and c3
+  
+    if(IsSigned){
+      cast<Instruction>(NewBinOp) -> setHasNoSignedWrap(true);
+      cast<Instruction>(NewBinOp) -> setHasNoUnsignedWrap(false);
+    }else{
+      cast<Instruction>(NewBinOp) -> setHasNoUnsignedWrap(true);
+      cast<Instruction>(NewBinOp) -> setHasNoSignedWrap(false);
+    }
+  
+
+  // Get the intrinsic function for MinMaxID
+  Type *Ty = II->getType();
+  Function *MinMaxFn = Intrinsic::getDeclaration(II->getModule(), MinMaxID, {Ty});
+
+  // Create new min/max intrinsic: MinMaxID(NewBinOp, C3) (not inserted)
+  Value *Args[] = {NewBinOp, Op1};
+  Instruction *NewMax = CallInst::Create(MinMaxFn, Args, "", nullptr);
+
+  return NewMax;
+}
+
 /// Match a sadd_sat or ssub_sat which is using min/max to clamp the value.
 Instruction *InstCombinerImpl::matchSAddSubSat(IntrinsicInst &MinMax1) {
   Type *Ty = MinMax1.getType();
@@ -2035,6 +2113,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     if (Instruction *I = moveAddAfterMinMax(II, Builder))
       return I;
 
+    // minmax(x << shamt , c << shamt) -> minmax(x, c) << shamt
+    if (Instruction *I = moveShiftAfterMinMax(II, Builder))  
+      return I;
+
+
     // minmax (X & NegPow2C, Y & NegPow2C) --> minmax(X, Y) & NegPow2C
     const APInt *RHSC;
     if (match(I0, m_OneUse(m_And(m_Value(X), m_NegatedPower2(RHSC)))) &&
diff --git a/llvm/test/Transforms/InstCombine/shift-binop.ll b/llvm/test/Transforms/InstCombine/shift-binop.ll
new file mode 100644
index 0000000000000..78e9c5ea21181
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/shift-binop.ll
@@ -0,0 +1,27 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i8 @src(i8 %arg0) {
+; CHECK-LABEL: @src(
+; CHECK-NEXT:    [[TMP1:%.*]] = shl nuw i8 [[ARG0:%.*]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = call i8 @llvm.umax.i8(i8 [[TMP1]], i8 16)
+; CHECK-NEXT:    ret i8 [[TMP2]]
+;
+  %1 = call i8 @llvm.umax.i8(i8 %arg0, i8 1)
+  %2 = shl nuw i8 %1, 1
+  %3 = call i8 @llvm.umax.i8(i8 %2, i8 16)
+  ret i8 %3
+}
+
+define i8 @tgt(i8 %arg0) {
+; CHECK-LABEL: @tgt(
+; CHECK-NEXT:    [[TMP1:%.*]] = shl nuw i8 [[ARG0:%.*]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = call i8 @llvm.umax.i8(i8 [[TMP1]], i8 16)
+; CHECK-NEXT:    ret i8 [[TMP2]]
+;
+  %1 = shl nuw i8 %arg0, 1
+  %2 = call i8 @llvm.umax.i8(i8 %1, i8 16)
+  ret i8 %2
+}
+
+declare i8 @llvm.umax.i8(i8, i8)

@github-actions
Copy link

github-actions bot commented May 19, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
View the diff from clang-format here.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index cc28ed8e1..75fac5377 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -2202,11 +2202,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     if (Instruction *I = moveAddAfterMinMax(II, Builder))
       return I;
 
-    // max(max(X,C1) binop C2, C3) -> max(X binop C2, max(C1 binop C2, C3)) -> max(X binop C2, C4)
-    if (Instruction *I = reduceMinMax(II, Builder,DL))  
+    // max(max(X,C1) binop C2, C3) -> max(X binop C2, max(C1 binop C2, C3)) ->
+    // max(X binop C2, C4)
+    if (Instruction *I = reduceMinMax(II, Builder, DL))
       return I;
 
-
     // minmax (X & NegPow2C, Y & NegPow2C) --> minmax(X, Y) & NegPow2C
     const APInt *RHSC;
     if (match(I0, m_OneUse(m_And(m_Value(X), m_NegatedPower2(RHSC)))) &&

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the alive2 proof into the PR description.

return nullptr;

// Check if BinOp is a left shift
if (BinOp != Instruction::Shl) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, you are trying to implement solution 2 suggested by me: #139786 (comment)

If it is the case, you should generalize it to handle most of binops (excluding div/rem), then use simplifyBinOp and simplifyBinaryIntrinsic to check if min/max(c1 binop c2, c3) folds to c3.

@@ -0,0 +1,27 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add more negative tests/vector tests/multi-use tests, as suggested by https://llvm.org/docs/InstCombineContributorGuide.html#tests?

@dtcxzyw dtcxzyw changed the title fix for Issue #139786 - Missed Optimization: max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2 [InstCombine] Fold max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2 May 19, 2025
; CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.umax.i8(i8 [[TMP1]], i8 16)
; CHECK-NEXT: ret i8 [[TMP2]]
;
%1 = call i8 @llvm.umax.i8(i8 %arg0, i8 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use named values.

"If it is the case, you should generalize it to handle most of binops (excluding div/rem), then use simplifyBinOp and simplifyBinaryIntrinsic to check if min/max(c1 binop c2, c3) folds to c3."
@Charukesh827
Copy link
Author

made most of suggested changes. Only thing is i didn't understand what has to be done for generalizing div, Please help me with that.

about the suggestion you gave:

"Solution1: Canonicalize max(x << shamt, c << shamt) into max(x, c) << shamt: https://alive2.llvm.org/ce/z/mQEDAQ
We already did similar things in moveAddAfterMinMax .

Solution2: Generalize to fold max(max(x, c1) binop c2, c3) —> max(x binop c2, c3)."

solution 1 was already available so i didn't make it. I only concentrated on solution 2

image

Alive2: https://alive2.llvm.org/ce/z/on2tJE

}

// Try canonicalize max(max(X,C1) binop C2, C3) -> max(X binop C2, C3)
static Instruction *moveShiftAfterMinMax(IntrinsicInst *II,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name should be updated.
In fact, this fold can be decomposed into two steps:

max(max(X,C1) binop C2, C3) -> // Associative laws
max(max(X binop C2, C1 binop C2), C3) -> // Commutative laws
max(X binop C2, max(C1 binop C2, C3)) -> // Constant fold
max(X binop C2, C4)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max(X, C1) binop C2 -> max(X binop C2, C1 binop C2) is not always safe for all binops. You can reuse the helper leftDistributesOverRight.

@dtcxzyw
Copy link
Member

dtcxzyw commented May 24, 2025

solution 1 was already available so i didn't make it.

c << shamt has been constant folded so that instcombine doesn't catch this pattern. Godbolt: https://godbolt.org/z/rndGjEE31

I only concentrated on solution 2

Yeah. I think it is a better solution.

Charukesh827 and others added 2 commits May 27, 2025 20:34
1)max(X, C1) binop C2 -> max(X binop C2, C1 binop C2) is not always safe for all binops. You can reuse the helper leftDistributesOverRight.
2)The function name should be updated.
In fact, this fold can be decomposed into two steps:

max(max(X,C1) binop C2, C3) -> // Associative laws
max(max(X binop C2, C1 binop C2), C3) -> // Commutative laws
max(X binop C2, max(C1 binop C2, C3)) -> // Constant fold
max(X binop C2, C4)
@Charukesh827
Copy link
Author

I wrote rightDistributesOverLeft function with the knowledge i have on this opt, please let me know is anything to be added or removed.

I considered only Add, Sub, Mul, Shl operation, as i was able to verify only these.

another optimization(i didn't write) dominates this optimization
Alive2 : https://alive2.llvm.org/ce/z/sMAnf_

}


static bool rightDistributesOverLeft(Instruction::BinaryOps ROp, bool HasNUW,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some header comments for this function.

Copy link
Member

@dtcxzyw dtcxzyw May 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide alive2 proof for this function ((X LOp Y) ROp Z -> (X ROp Z) LOp (Y ROp Z))?
Reference: #140526 (comment)

/// Associative laws max(max(X binop C2, C1 binop C2), C3) -> // Commutative
/// laws max(X binop C2, max(C1 binop C2, C3)) -> // Constant fold max(X binop
/// C2, C4)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

case Intrinsic::smin:
// Signed min/max distribute over addition if no signed wrap.
if (HasNSW && ROp == Instruction::Add)
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't hold for smin/smax: https://alive2.llvm.org/ce/z/XFf_U8

Comment on lines 1220 to 1222
const APInt *C;
if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_APInt(C)))) ||
!match(Op1, m_APInt(C)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const APInt *C;
if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_APInt(C)))) ||
!match(Op1, m_APInt(C)))
Constant *C2, *C3;
if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_ImmConstant(C2)))) ||
!match(Op1, m_ImmConstant(C3)))

Comment on lines 1247 to 1302
// Get constant values
APInt C1 = llvm::dyn_cast<llvm::ConstantInt>(InnerMinMaxInst->getOperand(1))
->getValue();
APInt C2 =
llvm::dyn_cast<llvm::ConstantInt>(BinOpInst->getOperand(1))->getValue();
APInt C3 =
llvm::dyn_cast<llvm::ConstantInt>(II->getArgOperand(1))->getValue();

// Constant fold: Compute C1 binop C2
APInt C1BinOpC2, Two, Pow2C2, C1TimesPow2C2;
bool overflow = false;
switch (BinOp) {
case Instruction::Add:
C1BinOpC2 = IsSigned ? C1.sadd_ov(C2, overflow) : C1.uadd_ov(C2, overflow);
break;
case Instruction::Mul:
C1BinOpC2 = IsSigned ? C1.smul_ov(C2, overflow) : C1.umul_ov(C2, overflow);
break;
case Instruction::Sub:
C1BinOpC2 = IsSigned ? C1.ssub_ov(C2, overflow) : C1.usub_ov(C2, overflow);
break;
case Instruction::Shl:
// Compute C1 * 2^C2
Two = APInt(C2.getBitWidth(), 2);
Pow2C2 = Two.shl(C2); // 2^C2
C1TimesPow2C2 = C1 * Pow2C2; // C1 * 2^C2

// Check C3 >= C1 * 2^C2
if (C3.ult(C1TimesPow2C2)) {
return nullptr;
} else {
C1BinOpC2 = C1.shl(C2);
}
break;
default:
return nullptr; // Unsupported binary operation
}

// Constant fold: Compute MinMaxID(C1 binop C2, C3) to get C4
APInt C4;
switch (MinMaxID) {
case Intrinsic::umax:
C4 = APIntOps::umax(C1BinOpC2, C3);
break;
case Intrinsic::umin:
C4 = APIntOps::umin(C1BinOpC2, C3);
break;
case Intrinsic::smax:
C4 = APIntOps::smax(C1BinOpC2, C3);
break;
case Intrinsic::smin:
C4 = APIntOps::smin(C1BinOpC2, C3);
break;
default:
return nullptr; // Unsupported intrinsic
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Get constant values
APInt C1 = llvm::dyn_cast<llvm::ConstantInt>(InnerMinMaxInst->getOperand(1))
->getValue();
APInt C2 =
llvm::dyn_cast<llvm::ConstantInt>(BinOpInst->getOperand(1))->getValue();
APInt C3 =
llvm::dyn_cast<llvm::ConstantInt>(II->getArgOperand(1))->getValue();
// Constant fold: Compute C1 binop C2
APInt C1BinOpC2, Two, Pow2C2, C1TimesPow2C2;
bool overflow = false;
switch (BinOp) {
case Instruction::Add:
C1BinOpC2 = IsSigned ? C1.sadd_ov(C2, overflow) : C1.uadd_ov(C2, overflow);
break;
case Instruction::Mul:
C1BinOpC2 = IsSigned ? C1.smul_ov(C2, overflow) : C1.umul_ov(C2, overflow);
break;
case Instruction::Sub:
C1BinOpC2 = IsSigned ? C1.ssub_ov(C2, overflow) : C1.usub_ov(C2, overflow);
break;
case Instruction::Shl:
// Compute C1 * 2^C2
Two = APInt(C2.getBitWidth(), 2);
Pow2C2 = Two.shl(C2); // 2^C2
C1TimesPow2C2 = C1 * Pow2C2; // C1 * 2^C2
// Check C3 >= C1 * 2^C2
if (C3.ult(C1TimesPow2C2)) {
return nullptr;
} else {
C1BinOpC2 = C1.shl(C2);
}
break;
default:
return nullptr; // Unsupported binary operation
}
// Constant fold: Compute MinMaxID(C1 binop C2, C3) to get C4
APInt C4;
switch (MinMaxID) {
case Intrinsic::umax:
C4 = APIntOps::umax(C1BinOpC2, C3);
break;
case Intrinsic::umin:
C4 = APIntOps::umin(C1BinOpC2, C3);
break;
case Intrinsic::smax:
C4 = APIntOps::smax(C1BinOpC2, C3);
break;
case Intrinsic::smin:
C4 = APIntOps::smin(C1BinOpC2, C3);
break;
default:
return nullptr; // Unsupported intrinsic
}
Constant *C1;
if (!match(InnerMinMaxInst->getRHS(), m_ImmConstant(C1))
return nullptr;
Constant *C1BinOpC2 = ConstantFoldBinaryOpOperands(BinOp, C1, C2, DL);
Constant *C4 = ConstantFoldBinaryIntrinsic(MinMaxID, C1BinOpC2, C3, C3->getType(), nullptr);

@Charukesh827
Copy link
Author

Changing the code to fit this below given alive2 proof

@Charukesh827
Copy link
Author

If we use this Alive2 proof then our aim "Fold max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2" itself becomes false, Shouldn't we do the Alive2 experiment with only one x and all other constant(like Alive2)?

@dtcxzyw
Copy link
Member

dtcxzyw commented Jun 18, 2025

If we use this Alive2 proof then our aim "Fold max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2" itself becomes false

It still holds for umin/umax. It is enough to address the motivating issue.

Shouldn't we do the Alive2 experiment with only one x and all other constant(like Alive2)?

No. We must ensure the transformation is valid for all possible inputs. In your case, both src and tgt return poison, which is always correct.
See also https://llvm.org/docs/InstCombineContributorGuide.html#use-generic-values-in-proofs

@Charukesh827
Copy link
Author

made all suggested changes, the rightDistributesOverLeft follows this alive proof

}

/// Returns weather the it holds for (X LOp Y) ROp Z -> (X ROp Z) LOp (Y ROp Z)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

bool HasNSW, Intrinsic::ID LOp) {
switch (LOp) {
case Intrinsic::umax:
if (HasNUW && (ROp == Instruction::AShr || ROp == Instruction::LShr ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ashr/lshr do not have nuw flags.

bool HasNSW, Intrinsic::ID LOp) {
switch (LOp) {
case Intrinsic::umax:
if (HasNUW && (ROp == Instruction::AShr || ROp == Instruction::LShr ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lshr/ashr do not have nuw flags.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add some positive/negative tests for all 13 cases? See also https://llvm.org/docs/InstCombineContributorGuide.html#tests


/// Try canonicalize max(max(X,C1) binop C2, C3) -> max(X binop C2, max(C1
/// binop C2, C3))
/// -> max(X binop C2, C4) //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// -> max(X binop C2, C4) //
/// -> max(X binop C2, C4)

Value *Op0 = II->getArgOperand(0), *Op1 = II->getArgOperand(1);
Value *InnerMax;
Constant *C2, *C3;
if (!match(Op0, m_OneUse(m_BinOp(m_Value(InnerMax), m_ImmConstant(C2)))) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BinOpInst->getOperand(1));

// Set overflow flags on new binary operation
if (auto *NewBinInst = dyn_cast<Instruction>(NewBinOp)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will assert when the binop is not an OverflowingBinaryOperator (e.g, a shift instruction).

Comment on lines +1256 to +1262
if (IsSigned) {
NewBinInst->setHasNoSignedWrap(true);
NewBinInst->setHasNoUnsignedWrap(false);
} else {
NewBinInst->setHasNoUnsignedWrap(true);
NewBinInst->setHasNoSignedWrap(false);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (IsSigned) {
NewBinInst->setHasNoSignedWrap(true);
NewBinInst->setHasNoUnsignedWrap(false);
} else {
NewBinInst->setHasNoUnsignedWrap(true);
NewBinInst->setHasNoSignedWrap(false);
}
NewBinInst->setHasNoSignedWrap(IsSigned);
NewBinInst->setHasNoUnsignedWrap(!IsSigned);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missed Optimization: max(max(x, c1) << c2, c3) —> max(x << c2, c3) when c3 >= c1 * 2 ^ c2

3 participants