Skip to content
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

Optimization for "~x + 1" to "-x" (#69003) #69600

Merged
merged 4 commits into from
Jun 7, 2022

Conversation

SkiFoD
Copy link
Contributor

@SkiFoD SkiFoD commented May 20, 2022

I added the optimization and marked the PR as a draft until it is checked by reviewers.

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label May 20, 2022
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 20, 2022
@ghost
Copy link

ghost commented May 20, 2022

Tagging subscribers to this area: @JulieLeeMSFT
See info in area-owners.md if you want to be subscribed.

Issue Details

I added the optimization and marked the PR as a draft until it is checked by reviewers.

Author: SkiFoD
Assignees: -
Labels:

area-CodeGen-coreclr, community-contribution

Milestone: -

// Fold (~x + 1) to -x.
if (op1->OperIs(GT_NOT) && op2->IsIntegralConst(1))
{
op1->SetOper(GT_NEG);
Copy link
Member

Choose a reason for hiding this comment

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

It's good practice here to call op1->SetVNsFromNode(add) after this to inherit the VN from the add. Also, I suggest we move this under OptimizationEnabled() below.

@JulieLeeMSFT JulieLeeMSFT added this to the 7.0.0 milestone May 20, 2022
@kasperk81
Copy link
Contributor

does it guard against ~x - 1 and cover 1 + ~x?

code

int A(int x) => ~x + 1;
int B(int x) => ~x + 50000;
int C(int x) => 1 + ~x;

int D(int x) => ~x - 1;
int E(int x) => ~x - 50000;
int F(int x) => -1 - ~x;;

expected

A(int):
        mov     eax, edi
        neg     eax
        ret
B(int):
        mov     eax, 49999
        sub     eax, edi
        ret
C(int):
        mov     eax, edi
        neg     eax
        ret
D(int):
        mov     eax, -2
        sub     eax, edi
        ret
E(int):
        mov     eax, -50000
        sub     eax, edi
        ret
F(int):
        mov     eax, edi
        ret

actual

A(int)
        mov     eax, edx
        not     eax
        inc     eax
        ret
B(int)
        mov     eax, edx
        not     eax
        add     eax, 0xc350
        ret
C(int)
        mov     eax, edx
        not     eax
        inc     eax
        ret
D(int):
        mov     eax, edx
        not     eax
        dec     eax
        ret
E(int):
        mov     eax, edx
        not     eax
        add     eax, 0xffff3cb0
        ret
F(int):
        mov     eax, edx
        not     eax
        neg     eax
        dec     eax
        ret

@jakobbotsch
Copy link
Member

does it guard against ~x - 1 and cover 1 + ~x?

We generally try to canonicalize constants to the right and restate subtractions with constants in terms of additions with constants.

It seems reasonable to me to handle more constants than 1 here. @SkiFoD, do you want to make that change?

@SkiFoD
Copy link
Contributor Author

SkiFoD commented May 21, 2022

@SkiFoD Sure, @kasperk81 thank you for your notice.

Comment on lines 12917 to 12933
// Fold (~x + GT_CNS_INT/GT_CNS_LNG) to ((GT_CNS_INT/GT_CNS_LNG - 1) - x).
// Ex. (~x + 2) = (1 - x)
if (op1->OperIs(GT_NOT) && op2->IsIntegralConst() &&
(op2->AsIntCon()->IconValue() > 1 || op2->AsIntCon()->IconValue() < 1))
{

add->SetOper(GT_SUB);

add->gtOp1 = op2;
GenTreeIntCon* constOp2 = op2->AsIntCon();
constOp2->SetValueTruncating(constOp2->IconValue() - 1);

add->gtOp2 = op1->AsOp()->gtGetOp1();
DEBUG_DESTROY_NODE(op1);

return add;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jakobbotsch I added this code to support compilation of (~x + 2) as shown here https://godbolt.org/z/TqTfq3dEq
I don't like this part tho, because it generates worse diffs and I don't like that I have to to put Const Node to the left.
Diff with this code
Diff without this code
What do you think of this part of code? Have I missed anything which makes the diffs worse?

Copy link
Member

Choose a reason for hiding this comment

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

I can see that other code in the JIT leaves -a + C alone:

runtime/src/coreclr/jit/morph.cpp

Lines 12883 to 12893 in f832309

// Do not do this if "op2" is constant for canonicalization purposes.
if (op1->OperIs(GT_NEG) && !op2->OperIs(GT_NEG) && !op2->IsIntegralConst() && gtCanSwapOrder(op1, op2))
{
add->SetOper(GT_SUB);
add->gtOp1 = op2;
add->gtOp2 = op1->AsOp()->gtGetOp1();
DEBUG_DESTROY_NODE(op1);
return add;
}

I would do the same here to avoid introducing this pattern with a constant on the left.
Besides, it is not clear that C - x is always preferred over ~x + C as it requires an extra register on x86/x64. The JIT does not have mechanism for picking between two forms like this depending on register pressure.
So I think you should go back to just optimizing ~x + 1. You can also experiment with ~x + C => -x + (C - 1) and see if that unlocks further optimizations (you can check occurrences in SPMI).

Comment on lines 12935 to 12937
// Fold (-1 - (~x)) to x
if ((op1->OperIs(GT_NEG) && op1->AsOp()->gtGetOp1()->OperIs(GT_NOT)) &&
op2->IsIntegralConst(-1))
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be better to do this as two separate transforms -(~x) => x + 1 and ~(-x) => x - 1. However, I think you should check if there are occurrences in SPMI to see if we should bother with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had played with it and tried different variations, but there were not any occurrences in SPMI, so I reverted the changes.

@SkiFoD SkiFoD requested a review from jakobbotsch May 23, 2022 09:08
Copy link
Member

@jakobbotsch jakobbotsch left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for trying some of the additional variations, even if it didn't turn out to be profitable. I'll launch some additional testing.

@jakobbotsch
Copy link
Member

/azp run Fuzzlyn

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@jakobbotsch
Copy link
Member

jakobbotsch commented May 23, 2022

The failures are known (#69659). @SkiFoD, I assume this is ready?

@SkiFoD SkiFoD marked this pull request as ready for review May 23, 2022 14:59
@SkiFoD SkiFoD changed the title Draft Optimization for "~x + 1" to "-x" (#69003) Optimization for "~x + 1" to "-x" (#69003) May 23, 2022
@jakobbotsch
Copy link
Member

Sorry, missed that you had marked this as ready. Thanks!

@jakobbotsch jakobbotsch merged commit 8b894ea into dotnet:main Jun 7, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jul 7, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants