[RFC] Support nneg flag with uitofp

Proposal

Add the nneg (non-negative) poison-generating flag to the uitofp instruction, with the following semantics:

  • uitofp nneg iN %x to fM returns poison if %x is negative.

A corollary is that uitofp nneg iN %x to fM is equivilent to sitofp iN %x to fM.

This would be implemented by having PossiblyNonNegInst support
uitofp (currently only supports zext).

Motivation

There are two motivations.

  1. This would allow us to canonicalize sitofp iN %x to fM to uitofp nneg iN %x to fM if we could prove %x is non-negative. This
    canonicalization can be useful in that it preserves more
    information about the result (namely that it is non-negative).

  2. This would help use chose the correct signedness for float ā†’ int
    casts in the backend which can have significant performance
    implications. For example, when the sitofp ā†’ uitofp
    canonicalization was introduced without the nneg
    flag
    it lead to
    up to 30% regressions for X86 targets on some
    benchmarks

    and a 3% reduction in instruction for RISCV across the
    llvm-test-suite
    . The
    point is that chosing the right cast signedness can be important,
    which this flag would enable us to do.

Alternatives

Just Re-Prove Signbit When We Want to Change Sign.

For example, If a target prefers a certain sign, just re-prove the
signbit of the input in the backend to try and change the cast between
sitofp ā†” uitofp.

This doesnā€™t really work as was evident when the canonicalization was
introduce without the nneg flag.

We often have better analysis in the middle-end allowing us to prove
non-negative in cases we canā€™t in the backend, further we throw out
information when lowering so its often simply unprovable later on.

Use a Different Flag Name

This proposal re-uses the nneg flag which is currently in use by
zext (for essentially the same reason of allowing the backend to
easily convert zext nneg ā†’ sext).

Personally I think the flag name/current usage fits this extension
well, but there may be confusions I am overlooking.

2 Likes

+1 for this.

Iā€™m mostly interested in avoiding UINT_TO_FP lowering as x86 codegen is shockingly poor.

No objections to the reuse of the nneg flag.

Iā€™m a bit confused about one aspect of this. The floating-point result of uitofp is, by definition, always non-negative. So, I donā€™t understand the claim that this canonicalization preserves information.

Iā€™m also inclined to take issue with the terminology here, and I suppose this applies to the existing zext nneg construct as well. LLVM IR is generally agnostic as to whether an integer value is signed or unsigned. So when you say ā€œpoison if %x is negativeā€ what you really mean is ā€œpoison if the sign bit of %x is setā€, right? This is a particularly relevant distinction for the case you are proposing here, because the uitofp instruction is one of the rare cases where the LLVM IR definition does make an explicit distinction between signed and unsigned integers: ā€œThe ā€˜uitofp ā€™ instruction regards value as an unsigned integer and converts that value to the ty2 type.ā€

Iā€™m not sure I see the value of the canonicalization. I understand that if you know the sign bit isnā€™t set you can make a much more efficient lowering of uitofp on x86-based targets, but we already have that same lowering for sitofp, so I donā€™t see the value of the sitofp ā†’ uitofp nneg canonicalization.

Iā€™m a bit confused about one aspect of this. The floating-point result of uitofp is, by definition, always non-negative. So, I donā€™t understand the claim that this canonicalization preserves information.

As in if we do sitofp ā†’ uitofp when we have proven the operand is non-negative, we may not be able to re-prove that later on (possibly due to erased instructions/context) or in general the backend has less sophisticated analysis. By attaching nneg when we do that (or really anytime we know the operand of uitofp is non-negative) we are preserving the information that we know so that later on we can re-use said information.

Iā€™m also inclined to take issue with the terminology here, and I suppose this applies to the existing zext nneg construct as well. LLVM IR is generally agnostic as to whether an integer value is signed or unsigned. So when you say ā€œpoison if %x is negativeā€ what you really mean is ā€œpoison if the sign bit of %x is setā€, right? This is a particularly relevant distinction for the case you are proposing here, because the uitofp instruction is one of the rare cases where the LLVM IR definition does make an explicit distinction between signed and unsigned integers: ā€œThe ā€˜uitofp ā€™ instruction regards value as an unsigned integer and converts that value to the ty2 type.ā€

What about icmp s{...}, fcmp, ashr, ā€¦ There are clearly cases where the signbit is valuable to know, both for the middle ends sake and in the backend.

Iā€™m not sure I see the value of the canonicalization. I understand that if you know the sign bit isnā€™t set you can make a much more efficient lowering of uitofp on x86-based targets, but we already have that same lowering for sitofp , so I donā€™t see the value of the sitofp ā†’ uitofp nneg canonicalization.

This is moreso canonicalization for caonicalizationā€™s sake (similiar to sext ā†’ zext). It may help with some folds, but even if we scrap the canonicalization part of it, having the nneg flag on uitofp allows the backend to make good codegen decisions, which it isnā€™t able to do now.

Your original RFC said that the nneg flag would let you know that the result was non-negative (which is always true for uitofp). Iā€™m not disputing the value of knowing that the signbit is not set on the operand. That makes a very big difference for this instruction on x86.

I was more concerned about the value of ā€œcanonicalization for canonicalizationā€™s sake.ā€ If I can prove that the signbit is zero, I could replace uitofp with sitofp and it would have the same effect as the new flag you are proposing for the purposes of lowering in the backend.

But as I was thinking about what the canonical form in this case should be, it led me to a realization that answers my question above (and I apologize if this is what you were saying and I just misunderstood). If we had the flag you are proposing, sitofp i64 %x to float and uitofp nneg i64 %x to float would be equivalent. I was focused on the uitofp form of this, which we know will always return a positive value regardless of the nneg flag, but I just realized that we donā€™t know whether sitofp will produce a positive or negative value, unless we backtrack and look at value analysis for the input operand.

Is that what you were saying?

In any event, Iā€™m convinced now.

1 Like

Is that what you were saying?

Yeah that is the idea. For zext nneg we have m_SExtLike. We will be able to have the same thing with uitofp nneg. Sorry for being unclear.

I think this is a reasonable extension ā€“ given that we already have zext nneg this is a very simple addition, so itā€™s reasonable to do it even if the value is not super high.