Skip to content

[InstCombine][Docs] Update InstCombine contributor guide #144228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions llvm/docs/InstCombineContributorGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,11 +404,32 @@ The use of TargetTransformInfo is only allowed for hooks for target-specific
intrinsics, such as `TargetTransformInfo::instCombineIntrinsic()`. These are
already inherently target-dependent anyway.

If some canonicalization narrow/widen the integer width of expressions, please
check `shouldChangeType()` first. Otherwise, we may evaluate the expression
in illegal/inefficient types.

For vector-specific transforms that require cost-modelling, the VectorCombine
pass can be used instead. In very rare circumstances, if there are no other
alternatives, target-dependent transforms may be accepted into
AggressiveInstCombine.

Generally, we prefer unsigned operations over signed operations in the middle-end, even
if signed operations are more efficient on some targets. The following is an incomplete
list of canonicalizations that are implemented in InstCombine:

| Original Pattern | Canonical Form | Condition |
|------------------------------|----------------------------|-------------------------------|
| `icmp spred X, Y` | `icmp samesign upred X, Y` | `sign(X) == sign(Y)` |
| `smin/smax X, Y` | `umin/umax X, Y` | `sign(X) == sign(Y)` |
| `sext X` | `zext nneg X` | `X >=s 0` |
| `sitofp X` | `uitofp nneg X` | `X >=s 0` |
| `ashr X, Y` | `lshr X, Y` | `X >=s 0` |
| `sdiv/srem X, Y` | `udiv/urem X, Y` | `X >=s 0 && Y >=s 0` |
| `add X, Y` | `or disjoint X, Y` | `(X & Y) != 0` |
| `mul X, C` | `shl X, Log2(C)` | `isPowerOf2(C)` |
| `select Cond1, Cond2, false` | `and Cond1, Cond2` | `impliesPoison(Cond2, Cond1)` |
| `select Cond1, true, Cond2` | `or Cond1, Cond2` | `impliesPoison(Cond2, Cond1)` |

### PatternMatch

Many transforms make use of the matching infrastructure defined in
Expand Down Expand Up @@ -531,6 +552,19 @@ need to add a one-use check for the inner instruction.
One-use checks can be performed using the `m_OneUse()` matcher, or the
`V->hasOneUse()` method.

### Flag handling

When possible, favour propagation of poison-generating flags like `nuw` and `nsw` since they may be
hard to salvage later. Avoid doing so if it introduces additional complexity (e.g. requires querying `willNotOverflow`
or KnownBits).

Be careful with in-place operand/predicate changes, as poison-generating flags may not be valid for new
operands. It is recommended to create a new instruction with careful handling of flags. If not
applicable, call `Instruction::dropPoisonGeneratingFlags()` to clear flags in a conservative manner.

Do not rely on fcmp's `nsz` flag to perform optimizations. It is meaningless for fcmp so it should not affect
the optimization.
Comment on lines +565 to +566
Copy link
Contributor

Choose a reason for hiding this comment

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

Is nsz meaningless for fcmp? I thought there are some optimizations already that take nsz on fcmp into account, with the property that -0. == +0.?

Copy link
Member Author

Choose a reason for hiding this comment

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

0.0 and -0.0 are considered equal, even if nsz is absent.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps I'm confused about something? See CmpInst::isEquivalence, which is used in some places:

// Returns true if either operand of CmpInst is a provably non-zero
// floating-point constant.
static bool hasNonZeroFPOperands(const CmpInst *Cmp) {
  auto *LHS = dyn_cast<Constant>(Cmp->getOperand(0));
  auto *RHS = dyn_cast<Constant>(Cmp->getOperand(1));
  if (auto *Const = LHS ? LHS : RHS) {
    using namespace llvm::PatternMatch;
    return match(Const, m_NonZeroNotDenormalFP());
  }
  return false;
}

// Floating-point equality is not an equivalence when comparing +0.0 with
// -0.0, when comparing NaN with another value, or when flushing
// denormals-to-zero.
bool CmpInst::isEquivalence(bool Invert) const {
  switch (Invert ? getInversePredicate() : getPredicate()) {
  case CmpInst::Predicate::ICMP_EQ:
    return true;
  case CmpInst::Predicate::FCMP_UEQ:
    if (!hasNoNaNs())
      return false;
    [[fallthrough]];
  case CmpInst::Predicate::FCMP_OEQ:
    return hasNonZeroFPOperands(this);
  default:
    return false;
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

equal is not equivalence. The latter one means one of the operands can be replaced by the other.

Copy link
Contributor

Choose a reason for hiding this comment

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

@artagnon Note that this does not actually checks the nsz flag, it checks whether the operands are non-zero. That's fine.


### Generalization

Transforms can both be too specific (only handling some odd subset of patterns,
Expand Down Expand Up @@ -558,6 +592,11 @@ guidelines.
use of ValueTracking queries. Whether this makes sense depends on the case,
but it's usually a good idea to only handle the constant pattern first, and
then generalize later if it seems useful.
* When possible, handle more canonical patterns as well. It is encouraged to avoid
potential phase-ordering issues. For example, if the motivating transform holds for
`add`, it also holds for `or disjoint`. See the canonicalization list above for details.
In most cases, it can be easily implemented with matchers like
`m_AddLike/m_SExtLike/m_LogicalAnd/m_LogicalOr`.

## Guidelines for reviewers

Expand Down
Loading