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

Bytecode round 12 #219

Closed
wants to merge 10 commits into from
Closed

Conversation

asotona
Copy link
Member

@asotona asotona commented Aug 30, 2024

  • Added test for operand values dominance revealed a bug in the BytecodeLift.
  • Fixed BytecodeLift revealed regression in the roundtrip stability.
  • Roundtrip instability was caused by a bug in the LocalsTypeMapper.

All the above is fixed and the roundtrip stability is restored.

Please review.

Thanks,
Adam


Progress

  • Change must not contain extraneous whitespace

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/babylon.git pull/219/head:pull/219
$ git checkout pull/219

Update a local copy of the PR:
$ git checkout pull/219
$ git pull https://git.openjdk.org/babylon.git pull/219/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 219

View PR using the GUI difftool:
$ git pr show -t 219

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/babylon/pull/219.diff

Webrev

Link to Webrev Comment

Sorry, something went wrong.

@bridgekeeper
Copy link

bridgekeeper bot commented Aug 30, 2024

👋 Welcome back asotona! A progress list of the required criteria for merging this PR into code-reflection will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Aug 30, 2024

@asotona This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

Bytecode round 12

Reviewed-by: psandoz

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been no new commits pushed to the code-reflection branch. If another commit should be pushed before you perform the /integrate command, your PR will be automatically rebased. If you prefer to avoid any potential automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the code-reflection branch, type /integrate in a new comment.

@asotona asotona marked this pull request as ready for review August 30, 2024 14:08
@openjdk openjdk bot added ready Pull request is ready to be integrated rfr Pull request is ready for review labels Aug 30, 2024
@mlbridge
Copy link

mlbridge bot commented Aug 30, 2024

Webrevs

Copy link
Member

@PaulSandoz PaulSandoz left a comment

Choose a reason for hiding this comment

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

It's good to see the value-based isDominatedBymethod being used, probably the first time in anger (not much prior testing on that).

How fastidious are you being about round trip stability? Given the following chain of events, where B == bytecode and M == code mode,

B1 -> M1 -> B2 -> M2 -> B3 -> M3

Do you want M1, M2 and M3 to be the same or just M2 and M3?

}
} catch (Throwable t) {
error("first lift", t);
}
}
}

private void verify(String category, CoreOp.FuncOp func) {
OpWriter.CodeItemNamerOption naming = null;
for (Body body : func.bodies()) {
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 possible to collapse all the loops by traversing the operations, passing in naming as the argument e.g.,

naming = func.traverse(naming, opVisitor((n, op) -> {
    for (Value v : op.operands()) {
        if (!op.result().isDominatedBy(v)) {
            if (n == null) { n = ... }
            ...
        }
    }
    return n;
}));

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I'll change it. Original idea was that more verifications will hook also on Body and Block level, however Op-level verification seem to be enough.

}
}

Set<Block> stopBlocks = new HashSet<>();
Copy link
Member

@PaulSandoz PaulSandoz Aug 30, 2024

Choose a reason for hiding this comment

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

If i got this right, we are dealing with an equivalent of an uninitialized variable, of a primitive type, in source that will be assigned in divergent code paths, and the compiler will check it's DA. In the model we currently require the modelling var op be initialized with a default constant value for the primitive type. We don't currently have a concept of an uninitialized value (which might help?), so we have to detect that all loads are dominated by stores, some of which are initializing stores (which detects the uninitialized case or a redundant initialization).

This specific bit of code after the dominated by checks determines if there is a path from n's declaring block to the entry block that does not pass through any of dom's declaring blocks? Why is that is important?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this is about uninitialized variables (of any type), where there are two or more assignments in divergent paths. Concept of uninitialized variable will definitely help to avoid obsolete default constants declaration. This is a detection of such situation, so the default initialization is skipped and the roundtrip is stable.

Copy link
Member Author

@asotona asotona Sep 2, 2024

Choose a reason for hiding this comment

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

This code detect there is no path from a VarLoadOp to the entry block that bypass the VarStoreOps. The default initialization is necessary if there is such path. I've just made it more abstract as a dominance test of a dominant set.

Copy link
Member

Choose a reason for hiding this comment

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

I still don't understand why the second check is needed, traversing the predecessors of n's declaring block. If n is not dominated by any values in set doms then presumably such traversal has to encounter the entry block?

The implementation of Value::isDominatedBy defers to block domination:

    public boolean isDominatedBy(Value dom) {
        if (this == dom) {
            return true;
        }

        if (declaringBlock() != dom.declaringBlock()) {
            return declaringBlock().isDominatedBy(dom.declaringBlock());
        }
...

Copy link
Member Author

Choose a reason for hiding this comment

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

Individual dominations are handled as you describe. It applies for cases like this:

entry -> var -> var store -> var load
            \
              > var store -> var load 

However following case needs a test for dominant set:

entry -> var -> var store -> var load
            \             /
              > var store

The var load is not dominated by any individual var store, however it is dominated by them.

The test should fail for example on following case:

entry -> var -> var store -> var load
             \            /     
                ---------

The reason why the second part is focused on blocks only is because ops cannot form a bypass inside one block.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Doh, of course, i get it now. I think what you are doing is the equivalent of DA analysis. Perhaps we can rename the method accordingly? e.g., isDefinitelyAssigned (noting it does not distinguish between assignment with a default value or non-assignment in source because we don't currently make that distinction in the model).

What does the bytecode verifier do when it encounters bytecode in the last case? I expect the verifier will be enhanced to support the new object initialization protocol.

Copy link
Member Author

Choose a reason for hiding this comment

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

Code with redundant default value assignment is valid, it just causes instability (the code grows with each round).

@asotona
Copy link
Member Author

asotona commented Sep 2, 2024

It's good to see the value-based isDominatedBymethod being used, probably the first time in anger (not much prior testing on that).

How fastidious are you being about round trip stability? Given the following chain of events, where B == bytecode and M == code mode,

B1 -> M1 -> B2 -> M2 -> B3 -> M3

Do you want M1, M2 and M3 to be the same or just M2 and M3?

Actually I'm testing only M2 vs M3. The first round is affected by strong stack use in the original code and BytecodeLift reflects that. BytecodeGenerator is not capable of complex predictive stack manipulations, it is limited to the one known value on stack. Second lift is then very different from the first one, however subsequent lifts should be stable.

@asotona
Copy link
Member Author

asotona commented Sep 5, 2024

Thank you for the review!
/integrate

@openjdk
Copy link

openjdk bot commented Sep 5, 2024

Going to push as commit 445a57f.

@openjdk openjdk bot added the integrated Pull request has been integrated label Sep 5, 2024
@openjdk openjdk bot closed this Sep 5, 2024
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Sep 5, 2024
@openjdk
Copy link

openjdk bot commented Sep 5, 2024

@asotona Pushed as commit 445a57f.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integrated Pull request has been integrated
Development

Successfully merging this pull request may close these issues.

None yet

2 participants