Soliciting opinions on JDK-8219412
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Mon Jan 9 15:54:41 UTC 2023
I agree with Brian. If we want to fix this and put our hands in the enum
switch classification, I think the best use of our time would be to
tweak enum switches to adopt the superior indy-based classification
approach, which should be immune to the issue you present.
Thanks
Maurizio
On 09/01/2023 15:35, Brian Goetz wrote:
> Another strategy is to use an `invokedynamic` as a switch classifier.
> (I believe there is even a bootstrap for this implemented somewhere.)
>
> The idea is that we lower a switch
>
> switch (e) [
> case A: …
> case B: …
> …
> }
>
> Into a compact switch on int, and use an indy to map the target to a
> case number:
>
> switch (indy [BSM=EnumClassifier, type=(E)I, args = [ E.class,
> “A”, “B”, … ]) {
> case 1: …
> Case 2: …
> }
>
> The indy bootstrap takes as a dynamic argument the switch operand
> (enum value), and returns an int corresponding the case number.
> The static argument list identifies the enum constants (by string)
> corresponding to the case numbers. The bootstrap builds the array
> that we would have built at compile time, curries it onto a method
> handle, and wraps the method handle in a ConstantCallSite. Then the
> array is only built the first time we invoke the switch. Thereafter
> we get similar behavior to the current scheme, but without static
> initializers.
>
>
>
>
>
>> On Jan 9, 2023, at 10:02 AM, Archie Cobbs <archie.cobbs at gmail.com> wrote:
>>
>> I'm looking for advice/opinions on the best way to
>> address JDK-8219412 - Eager enum class initialization with enum switch.
>>
>> The problem stems from how the compiler implements switches on enum.
>> The obvious simple approach is to get the enum's ordinal() value and
>> then revert to a normal integer value switch. However, this snapshots
>> the ordinal values at compile-time, and thus can fail if
>> the Enum class is recompiled later.
>> Instead the compiler uses a lookup table. This table is dynamically
>> constructed at runtime by a static initializer in a synthetic class.
>> When compiling Foo.java, a separate lookup table is created for each
>> enum type that is switched on anywhere in Foo.java. These lookup
>> tables are all put into a single synthetic class Foo$1 and are all
>> initialized at the same time, in a single static initializer.
>>
>> Now suppose Foo contains switches on enum types E1, E2, and E3 and a
>> switch on E1 is encountered. This will cause classes E2 and E3 to be
>> initialized, even though there is no 'first use' of either class.
>> This is the bug.
>>
>> Strategy #1 - Put each lookup table into a separate synthetic class.
>>
>> You can see the reverse of this fix in this diff.
>>
>> The upside of this approach is that it fixes the bug without changing
>> the approach and therefore presumably minimally affecting performance.
>>
>> The downside is an increase in the number of synthetic classes
>> created: for every enum type switched on in Foo.java but declared in
>> some other source file, there is a synthetic class
>> created: Foo$1, Foo$2, etc. Is that a show-stopper??
>>
>> The total amount of code generated is basically the same, it's just
>> spread over multiple class files instead of one.
>>
>> Strategy #2 - Switch on String identifiers instead of ordinal values
>>
>> This approach is straightforward, however the performance suffers a
>> good bit in my tests. Probably not an option.
>>
>> Strategy #3 - Don't rely on a static initializer to build the mapping
>> tables
>>
>> This approach would mean not relying on class initialization to
>> trigger building the mapping tables, so we could build each table
>> separately on demand. The synthetic class would look something like this:
>>
>> synthetic /* volatile ? */ int[] $EnumMap$E1;
>> synthetic /* volatile ? */ int[] $EnumMap$E2;
>> synthetic /* volatile ? */ int[] $EnumMap$E3;
>>
>> static int[] getEnumMap$E1() {
>> if ($EnumMap$E1 == null) {
>> /* build map here */
>> }
>> return $EnumMap$E1;
>> }
>>
>> static int[] getEnumMap$E2() {
>> if ($EnumMap$E2 == null) {
>> /* build map here */
>> }
>> return $EnumMap$E2;
>> }
>>
>> static int[] getEnumMap$E3() {
>> if ($EnumMap$E3 == null) {
>> /* build map here */
>> }
>> return $EnumMap$E3;
>> }
>>
>> Strategy #4 - Other ideas?
>>
>> Thanks,
>> -Archie
>>
>> --
>> Archie L. Cobbs
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230109/b313cc5d/attachment.htm>
More information about the compiler-dev
mailing list