This is an archive of the discontinued LLVM Phabricator instance.

[LV][IVDescriptors] Fix recurrence identity element for FMin and FMax reductions.
ClosedPublic

Authored by karthiksenthil on Nov 1 2022, 6:20 PM.

Details

Summary

For a min and max reduction idioms, the identity (i.e. neutral) element
should be datatype's highest and lowest possible values respectively.
Current implementation in IVDescriptors incorrectly returns -Inf for FMin
reduction and +Inf for FMax reduction. This patch fixes this bug which
was causing incorrect reduction computation results in loops vectorized
by LV.

Diff Detail

Event Timeline

karthiksenthil created this revision.Nov 1 2022, 6:20 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 1 2022, 6:20 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
karthiksenthil requested review of this revision.Nov 1 2022, 6:20 PM
dmgreen added a subscriber: dmgreen.Nov 2 2022, 2:02 AM

Do we have any tests for Max as well as Min? It looks like the llvm/test/Transforms/LoopVectorize/AArch64/scalable-reduction-inloop-cond.ll test may need updating.

spatel added a comment.Nov 2 2022, 9:26 AM

Should these be guarded with an FMF check?

The enum definition says:

FMin,       ///< FP min implemented in terms of select(cmp()).
FMax,       ///< FP max implemented in terms of select(cmp()).

So if we have a NaN input:
fmin(NaN, Inf) --> select (fcmp olt NaN, Inf), NaN, Inf --> Inf (so the NaN input did not survive)

Is it possible to write unit-tests to check these directly? I see a llvm/unittests/Analysis/IVDescriptorsTest.cpp test file...

karthiksenthil added a reviewer: dmgreen.

Thanks for the reviews! I've addressed the comments below -

Do we have any tests for Max as well as Min? It looks like the llvm/test/Transforms/LoopVectorize/AArch64/scalable-reduction-inloop-cond.ll test may need updating.

I've added new tests in llvm/unittests/Analysis/IVDescriptorsTest.cpp to cover both FMin and FMax reductions. Updated the LIT test as well.

Should these be guarded with an FMF check?

The enum definition says:

FMin,       ///< FP min implemented in terms of select(cmp()).
FMax,       ///< FP max implemented in terms of select(cmp()).

So if we have a NaN input:
fmin(NaN, Inf) --> select (fcmp olt NaN, Inf), NaN, Inf --> Inf (so the NaN input did not survive)

Do you mean that identity value computation for FMin/FMax should be guarded with a FMF.noNaNs() check? What would be the identity value when the flag is absent?

Is it possible to write unit-tests to check these directly? I see a llvm/unittests/Analysis/IVDescriptorsTest.cpp test file...

I've updated the test file with new tests, I can extend this for testing missing nnan flag scenario as well.

spatel added a comment.Nov 2 2022, 2:36 PM

Should these be guarded with an FMF check?

The enum definition says:

FMin,       ///< FP min implemented in terms of select(cmp()).
FMax,       ///< FP max implemented in terms of select(cmp()).

So if we have a NaN input:
fmin(NaN, Inf) --> select (fcmp olt NaN, Inf), NaN, Inf --> Inf (so the NaN input did not survive)

Do you mean that identity value computation for FMin/FMax should be guarded with a FMF.noNaNs() check? What would be the identity value when the flag is absent?

Yes, I'm not sure what it means if we don't have FMF.noNaNs(). Is it possible to create this recurrence without that FMF? If not, can we assert that FMF.noNaNs() is set?

Should these be guarded with an FMF check?

The enum definition says:

FMin,       ///< FP min implemented in terms of select(cmp()).
FMax,       ///< FP max implemented in terms of select(cmp()).

So if we have a NaN input:
fmin(NaN, Inf) --> select (fcmp olt NaN, Inf), NaN, Inf --> Inf (so the NaN input did not survive)

Do you mean that identity value computation for FMin/FMax should be guarded with a FMF.noNaNs() check? What would be the identity value when the flag is absent?

Yes, I'm not sure what it means if we don't have FMF.noNaNs(). Is it possible to create this recurrence without that FMF? If not, can we assert that FMF.noNaNs() is set?

Looks like the following code in RecurrenceDescriptor::isRecurrenceInstr guarantees that nnan flag is set for FMin/FMax reductions -

case Instruction::FCmp:
case Instruction::ICmp:
case Instruction::Call:
  if (isSelectCmpRecurrenceKind(Kind))
    return isSelectCmpPattern(L, OrigPhi, I, Prev);
  if (isIntMinMaxRecurrenceKind(Kind) ||
      (((FuncFMF.noNaNs() && FuncFMF.noSignedZeros()) ||
        (isa<FPMathOperator>(I) && I->hasNoNaNs() &&
         I->hasNoSignedZeros())) &&
       isFPMinMaxRecurrenceKind(Kind)))
    return isMinMaxPattern(I, Kind, Prev);

I've added asserts in the latest diff to check that FMF.noNaNs() is set.

spatel added a comment.Nov 3 2022, 5:05 AM

I've added asserts in the latest diff to check that FMF.noNaNs() is set.

Thanks - should it also assert for noSignedZeros?

I've added asserts in the latest diff to check that FMF.noNaNs() is set.

Thanks - should it also assert for noSignedZeros?

Yes, I think it would be better to keep this assert in-sync with the checks in RecurrenceDescriptor::isRecurrenceInstr. I have updated the asserts and unit-tests in latest diff.

spatel accepted this revision.Nov 3 2022, 10:41 AM

LGTM

This revision is now accepted and ready to land.Nov 3 2022, 10:41 AM

Rebase to llvm trunk. No functional updates.

Thanks for the reviews and approval! I don't have access to commit this change. @spatel can you commit this revision to llvm trunk?