Possible bug: compiler generating `ldc` instead of `sipush` and `iconst_m1` for characters in range [32768; 65535]

JARvis PROgrammer mrjarviscraft at gmail.com
Thu Aug 8 17:16:05 UTC 2019


Hi there, it seems that the javac compiler generates non-optimal bytecode
for pushing values of (compile-time) type `char`.
In order to test it I've first written a class which has a method accepting
`char`s which writes them to a file (so that results can be easily
compared):

private static void consumeChar(final char character) throws IOException {
    try (final BufferedWriter writer = new BufferedWriter(new
FileWriter(new File("Javac.txt"), true))) {
        writer.write(Character.toString(character));
    }
}

And then added code to `main` which simply invokes this method for all
corner cases and nearby char values {(char) 0, (char) 1, (char) 2, (char)
3, (char) 4, (char) 5, (char) 6, (char) 7, (char) 126, (char) 127, (char)
128, (char) 129, (char) 32766, (char) 32767, (char) 32768, (char) 32769,
(char) 65534, (char) 65535}. After this the class was compiled using
`javac` (both tested on release 11 and early-access 13).
The resulting bytecode uses *iconst#* for range [(char) 0; (char) 5],
*bipush* for range from [(char) 6; (char) 127], *sipush* for range
[(char) 128; (char) 32767] and (strangely) *ldc* for range [(char) 32768;
(char) 65535].
In order to check if the strange behavour is not the only way to achieve
pushing "big" `char` values onto the stack, I've recreated the similar
class using ObjectWeb ASM using *iconst_m1* instruction for pusing ((char)
65535) and *sipush* (with negative values) for pushing values of range
[(char) 32768; (char) 65534].
The following function describes this behaviour:

private static void pushChar(final MethodVisitor method, final int charCode) {
    switch (charCode) {
        case 65535: {
            method.visitInsn(ICONST_M1);
            break;
        }
        case 1: {
            method.visitInsn(ICONST_1);
            break;
        }
        case 2: {
            method.visitInsn(ICONST_2);
            break;
        }
        case 3: {
            method.visitInsn(ICONST_3);
            break;
        }
        case 4: {
            method.visitInsn(ICONST_4);
            break;
        }
        case 5: {
            method.visitInsn(ICONST_5);
            break;
        } default: {
            if (charCode <= Byte.MAX_VALUE) method.visitIntInsn(BIPUSH, charCode
            else method.visitIntInsn(SIPUSH, (short) charCode);
        }
    }
}

(PS, in theory, *bipush* might also be aplicable for some values where
*sipush* is used, but I didn't test it)
The generated class once run via java (release 11 and early-access 13 were
used) generated the file with content equal to the one of the
javac-compiled class.

And so, it seems to be a bug that `javac` uses *ldc* instruction instead of
more primitive ones while those may handle all range of characters.

All files used for test purposes (including javap call results) are
attached to this message:
JavacMain.java - source code of class compiled via javac
JavacMain.class - compiled JavacMain.java (javac -g:none --enable-preview)
JavacMain.javap - disassembled JavacMain.class (javac  -v -p -s -c)
Javac.txt - file written by running JavacMain.class

AsmMain_Generator.java - source code of class generating AsmMain.class
AsmMain.class - generated replica of JavacMain.class using *iconst_m1*
instruction
for pusing ((char) 65535) and *sipush* (with negative values) for pushing
values of range [(char) 32768; (char) 65534]
Asm  Main.javap - disassembled  AsmMain.class (javac  -v -p -s -c)
Asm.txt - file written by running AsmMain.class
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/attachment-0001.html>
-------------- next part --------------
 ~????????
-------------- next part --------------
A non-text attachment was scrubbed...
Name: JavacMain.java
Type: application/octet-stream
Size: 1190 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/JavacMain-0001.java>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: JavacMain.class
Type: application/octet-stream
Size: 904 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/JavacMain-0001.class>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: JavacMain.javap
Type: application/octet-stream
Size: 8513 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/JavacMain-0001.javap>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: AsmMain_Generator.java
Type: application/octet-stream
Size: 9158 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/AsmMain_Generator-0001.java>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: AsmMain.class
Type: application/octet-stream
Size: 883 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/AsmMain-0001.class>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: AsmMain.javap
Type: application/octet-stream
Size: 8252 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20190808/32126599/AsmMain-0001.javap>
-------------- next part --------------
 ~????????


More information about the compiler-dev mailing list