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

8244313: [lworld] Evolve javac's code generation to match scheme documented in SoV #386

Closed
wants to merge 1 commit into from

Conversation

@sadayapalam
Copy link
Collaborator

@sadayapalam sadayapalam commented Apr 16, 2021

Generate code according to the scheme outlined in the Members section of https://cr.openjdk.java.net/~briangoetz/valhalla/sov/04-translation.html


Progress

  • Change must not contain extraneous whitespace

Issue

  • JDK-8244313: [lworld] Evolve javac's code generation to match scheme documented in SoV

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.java.net/valhalla pull/386/head:pull/386
$ git checkout pull/386

Update a local copy of the PR:
$ git checkout pull/386
$ git pull https://git.openjdk.java.net/valhalla pull/386/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 386

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

Using diff file

Download this PR as a diff file:
https://git.openjdk.java.net/valhalla/pull/386.diff

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented Apr 16, 2021

👋 Welcome back sadayapalam! A progress list of the required criteria for merging this PR into lworld 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 openjdk bot commented Apr 16, 2021

@sadayapalam 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:

8244313: [lworld] Evolve javac's code generation to match scheme documented in SoV

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 lworld 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 lworld branch, type /integrate in a new comment.

@mlbridge
Copy link

@mlbridge mlbridge bot commented Apr 16, 2021

Webrevs

@sadayapalam
Copy link
Collaborator Author

@sadayapalam sadayapalam commented Apr 16, 2021

Hotspot compiler folk,

This change set is failing a bunch of tests compiler/valhalla/inlinetypes. I believe this is due to problems in the hotspot compiler.
Analysis here:

  1. These three tests generate SIGSEGV:
    - TestBasicFunctionality.java
    - TestC2CCalls.java
    - TestLWorld.java
  2. These two tests trigger a RuntimeException: Unexpected compilation level for ...
    - TestCallingConvention.java
    - TestOnStackReplacement.java
  3. This test generates a CCE in compiled code, bytecode looks reasonable:
    - TestCallingConventionC1.java
  4. This test triggers a runtime assertion: "RuntimeException: assertEquals: expected [compiler.valhalla.inlinetypes.TestNullableArrays$Complex re=291.0 im=291.0] to equal [compiler.valhalla.inlinetypes.TestNullableArrays$Complex re=291.0 im=169653.0]"
    - TestNullableArrays.java

Thanks for looking into these. If you can provide a fix in time, I can include it in the integration.

@sadayapalam
Copy link
Collaborator Author

@sadayapalam sadayapalam commented Apr 16, 2021

hotspot-runtime folks,

This patch is failing one test: runtime/valhalla/inlinetypes/CircularityTest.java, I think under the code generation scheme outlined in https://cr.openjdk.java.net/~briangoetz/valhalla/sov/04-translation.html, this test's expectation of a circularity being found is not valid per a cursory glance, Thanks for looking into it - If you can provide a fix in time, I can include it in the integration.

@sadayapalam
Copy link
Collaborator Author

@sadayapalam sadayapalam commented Apr 16, 2021

JDK corelibs folks,

This patch is failing two tests that make a bad assumption that were only true because javac was not following the recommended code generation scheme outlined in the Members section of https://cr.openjdk.java.net/~briangoetz/valhalla/sov/04-translation.html

The failing tests are:

valhalla/valuetypes/QTypeDescriptorTest.java
valhalla/valuetypes/Reflection.java

The failures stem from getDeclaredMethod not finding methods it used to - in the new scheme, those methods are emitted into the reference projection class

Thanks for looking into it - If you can provide a fix in time, I can include it in the integration.

Copy link
Collaborator

@mcimadamore mcimadamore left a comment

I have some question regarding constant pool switching

@@ -527,4 +542,29 @@ void reset() {
bootstrapMethods.clear();
pool.reset();
}

void switchPool() {
Copy link
Collaborator

@mcimadamore mcimadamore Apr 16, 2021

Choose a reason for hiding this comment

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

Can you explain the rationale behind going down this path, instead of creating a new pool? E.g. could we call ClassWriter.writeClass on two symbols, instead of having a single call generate two things by using this constant pool switching? It feels like I'm missing something here...

Copy link
Collaborator Author

@sadayapalam sadayapalam Apr 19, 2021

Choose a reason for hiding this comment

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

Obviously, different approaches possible.

I am documenting my chain of thoughts/observations here:

- Each invocation of the javac compiler, gets a single ClassWriter instance and a single instance of the byte code generator Gen.
- The single instance of the code generator Gen instantiates a single instance of the PoolWriter
- The Pool writer manages all the state associated with the constant pool
- Given a single invocation of the compiler may call for compiling numerous classes and generating numerous class files, and since each class file has its own constant pool, as each JCClassDecl advances through Gen and gets written out into a class file, the ClassWriter calls PoolWriter.reset() after finished with a writing out a class file. This is so as to allow for the slate to be wiped clean in preparation for the next JCClassDecl being fed to Gen.

It is conceivable that we could have bifurcated the the primitive class source declaration into two completely different ASTs, one rooted at a JCClassDecl for the value projection class and another rooted at a at a JCClassDecl for the reference projection class, each with its own members according the members sorting algorithm outlined in SoV4 and inject them into the pipeline at the suitable stage. Each JCClassDecl advances through the byte code generator Gen and the ClassWriter on its own, gets its own pool and gets written out on its own in this block in com.sun.tools.javac.main.JavaCompiler#genCode

JavaFileObject genCode(Env<AttrContext> env, JCClassDecl cdef) throws IOException {
        try {
            if (gen.genClass(env, cdef) && (errorCount() == 0))
                return writer.writeClass(cdef.sym);
        } catch (ClassWriter.PoolOverflow ex) {
            log.error(cdef.pos(), Errors.LimitPool);
        } catch (ClassWriter.StringOverflow ex) {
            log.error(cdef.pos(),
                      Errors.LimitStringOverflow(ex.value.substring(0, 20)));
        } catch (CompletionFailure ex) {
            chk.completionError(cdef.pos(), ex);
        }
        return null;
    }

The three abstractions involved i.e Gen, PoolWriter and ClassWriter each is "aware" of the fact that a single JCClassDecl that is advancing through the pipeline is going to be bifurcated down the road at the deep end and handshake with each other to achieve this end goal.

Alternate implementations where one or more these abstractions are completely oblivious to the bifurcation taking place are possible.

Which approach is better may boil down to a matter of taste. I found it extremely simple and straightforward to enhance the PoolWriter to operate on bipartite pool by having each of these three abstractions be "clued into the goings on".

Copy link
Collaborator

@mcimadamore mcimadamore Apr 19, 2021

Choose a reason for hiding this comment

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

Trying to understand - is switchPool called only once?
If so, can't we replace switchPool by simply creating a new PoolWriter? I know that, up to now, we had only one instance of PW, but of the various abstractions, PW seems the one that is more amenable in terms of spawning new instances?

Copy link
Collaborator

@mcimadamore mcimadamore Apr 19, 2021

Choose a reason for hiding this comment

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

Ok, I see - switch pool is also called from ClassWriter whenever we need to add a new symbol to the pool and we see that the symbol belongs to a different projection.

I guess the approach that seems intuitively more natural to me (as it requires less modifiable state) is one where we create two ClassWriter instances for the same symbol/AST - but one on the reference projection polarity, the other on the inline polarity. Symbols that do not belong to the correct polarity are just skipped.

I agree that splitting JCClassDecl seems like overkill for this.

Copy link
Collaborator

@mcimadamore mcimadamore Apr 19, 2021

Choose a reason for hiding this comment

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

I now see where my "intuitive" idea falls apart: Gen is doing some of the work which causes entries to be written to the pool (e.g. visitMethod). E.g. by the time we get to ClassWriter, the pool is already partially written. This is probably what motivated the split at that level. So, I agree, it's either current approach, with mutable pool writer, or one where we have two JCClassDecl (and two Gen passes). Ugh.

@sadayapalam
Copy link
Collaborator Author

@sadayapalam sadayapalam commented Apr 19, 2021

The comment in com.sun.tools.javac.jvm.ClassWriter#writeClassFile

        poolWriter.writePool(out);
        poolWriter.reset(); // to save space

misstates what we are doing there IMO :)

@sadayapalam
Copy link
Collaborator Author

@sadayapalam sadayapalam commented May 24, 2021

This work is abandoned in favor of the single class file model pursued under https://bugs.openjdk.java.net/browse/JDK-8265423

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
2 participants