This is an archive of the discontinued LLVM Phabricator instance.

[DAGCombiner][AArch64] Enhance to fold CSNEG into CSINC instruction
ClosedPublic

Authored by Allen on Feb 6 2022, 9:20 PM.

Details

Summary

Perform the scalar expression combine in the form of:

CSNEG(1, c, cc) + b  =>  cc  ? b+1 : b-c => CSINC(b-c, b, !cc)
CSNEG(c, -1, cc) + b =>  cc  ? b+c : b+1 => CSINC(b+c, b, cc)

Fix https://github.com/llvm/llvm-project/issues/53071

Diff Detail

Event Timeline

Allen created this revision.Feb 6 2022, 9:20 PM
Allen requested review of this revision.Feb 6 2022, 9:20 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 6 2022, 9:20 PM
Allen added a comment.Feb 8 2022, 5:31 AM

@dmgreen can you help to give some advice, thanks!

The code is very similar to the performAddCSelIntoCSinc method. Can they share the same code, with the differences for CSNeg accounted for?

Allen added a comment.EditedFeb 8 2022, 6:46 PM

The code is very similar to the performAddCSelIntoCSinc method. Can they share the same code, with the differences for CSNeg accounted for?

Thanks for you comment again! I has tried it before, it seems much complicated, I'll glad to update if you think it's better to share ?

  // The CSEL should include a const one operand, and the CSNEG should include
  // One or NegOne operand.
  ConstantSDNode *CTVal = dyn_cast<ConstantSDNode>(LHS.getOperand(0));
  ConstantSDNode *CFVal = dyn_cast<ConstantSDNode>(LHS.getOperand(1));
  if (!CTVal || !CFVal)
    return SDValue();

  if (!(LHS.getOpcode() == AArch64ISD::CSEL &&
        (CTVal->isOne() || CFVal->isOne())) ||
      !(LHS.getOpcode() == AArch64ISD::CSNEG &&
        (CTVal->isOne() || CFVal->isAllOnes())))
    return SDValue();

  // Switch CSEL(1, c, cc) to CSEL(c, 1, !cc)
  if (LHS.getOpcode() == AArch64ISD::CSEL &&
      CTVal->isOne() && !CFVal->isOne()) {
    std::swap(CTVal, CFVal);
    AArch64CC = AArch64CC::getInvertedCondCode(AArch64CC);
  }

  SDLoc DL(N);
  // Switch CSNEG(1, c, cc) to CSNEG(-c, -1, !cc)
  if (LHS.getOpcode() == AArch64ISD::CSNEG &&
    CTVal->isOne() && !CFVal->isAllOnes()) {
    int64_t C = -1 * CFVal->getSExtValue();
    CTVal = cast<ConstantSDNode>(DAG.getConstant(C, DL, VT));
    CFVal = cast<ConstantSDNode>(DAG.getConstant(-1, DL, VT));
    AArch64CC = AArch64CC::getInvertedCondCode(AArch64CC);
  }

...

  assert((LHS.getOpcode() == AArch64ISD::CSEL && CFVal->isOne() ||
          LHS.getOpcode() == AArch64ISD::CSNEG && CFVal->isAllOnes()) &&
         "Unexpected constant value");
Allen edited the summary of this revision. (Show Details)Feb 9 2022, 4:48 PM

Hmm. I feel like it shares more code than it needs special cases? Maybe try and give it a go, and we can see if it looks too bad.

(I had in the past had the idea of removing AArch64ISD::CSNEG/CSINC/CSINV - they are mostly used only for constants as far as I understand and could just as well use CSEL directly. Either that or make sure of them more, canonicalizing more patterns to use them. I don't know what the tradeoffs of those are though. Less target dependant nodes can be a good thing, but more aggressively targeting the nodes we have as opposed to leaving it to chance may be good too. That's just an aside though).

Allen updated this revision to Diff 407750.Feb 10 2022, 7:47 PM

fix pre-merge format checks

Thanks. I think something might not be right in the new logic.
(add y, (csneg 1, c, eq)) is eq ? y+1 : y-c. Which should be (csinc (sub y, c), y, ne)
(add y, (csneg c, -1, eq)) is eq ? y+c : y+1. Which should be (csinc (add y, c), y, eq)
I may be wrong with all these double negatives though, correct me if that looks wrong. I would expect the assembly to be sub w8, w1, #1; csinc w0, w8, w1

Allen updated this revision to Diff 408130.EditedFeb 11 2022, 6:23 PM

Thanks , you are right, good catch. https://godbolt.org/z/nzvanY3GT

Allen edited the summary of this revision. (Show Details)Feb 11 2022, 6:32 PM
dmgreen accepted this revision.Feb 14 2022, 12:56 AM

Thanks, LGTM with a couple of suggestions.

llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
14805

Probably better to avoid the uint64_t and use APInt directly.

14806

This can use DAG.getAllOnesConstant(..)

llvm/test/CodeGen/AArch64/aarch64-isel-csinc.ll
132

Can you add i64 variants of these tests, maybe with different conditions? You can also remove the dso_local.

This revision is now accepted and ready to land.Feb 14 2022, 12:56 AM
Allen updated this revision to Diff 408685.Feb 14 2022, 6:34 PM

update according review

Allen updated this revision to Diff 408692.Feb 14 2022, 7:27 PM
Allen marked 2 inline comments as done.

add new test case of type i64

Allen updated this revision to Diff 408722.Feb 14 2022, 11:30 PM

rebase to new head

This revision was landed with ongoing or failed builds.Feb 15 2022, 5:40 PM
This revision was automatically updated to reflect the committed changes.