<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body><div style="font-family: sans-serif;"><div class="markdown" style="white-space: normal;">
<p dir="auto">On 13 Oct 2023, at 12:55, Liam Miller-Cushon wrote:</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">Thanks for the comments, that is a very neat idea!</p>
<p dir="auto">I was labouring under a misunderstanding of what the verifier constraints
<br>
are. My understanding is that at the Java language level, each static final
<br>
has to be definitely assigned at the end of the static initializer (JLS
<br>
8.3.1.2), but the VM has fewer restrictions. Is the only constraint that
<br>
putstatic is only allowed to write to static final fields inside clinit,
<br>
and there is no requirement that static finals are definitely assigned in
<br>
the clinit, so using Unsafe to initialize the fields is fair game?</p>
</blockquote></div>
<div class="markdown" style="white-space: normal;">
<p dir="auto">Yes, that is the case.</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">I have a rough draft of your approach here:
<br>
<a href="https://github.com/openjdk/jdk/pull/16191" style="color: #777777;">https://github.com/openjdk/jdk/pull/16191</a></p>
<p dir="auto">* It uses reflection and Unsafe.staticFieldOffset for the JVM method.</p>
<p dir="auto">* I took a short-cut and disabled generation of values() array. It would be
<br>
possible to split the values() array across multiple methods, but that
<br>
would still require additional constant pool entries, and as you described
<br>
the constant pool size ends up being the limiting factor. I think the
<br>
values() array could still be a good use of indy or condy.</p>
</blockquote></div>
<div class="markdown" style="white-space: normal;">
<p dir="auto">Yes, condy would work.  The values method would then be generated like this:</p>
<pre style="margin-left: 15px; margin-right: 15px; padding: 5px; background-color: #F7F7F7; border-radius: 5px 5px 5px 5px; overflow-x: auto; max-width: 90vw;"><code style="margin: 0; border-radius: 3px; background-color: #F7F7F7; padding: 0px;">ldc Condy{makeValuesArrayBuilder} : MethodHandle
invokevirtual {MethodHandle::invoke()ThisEnum[]}
areturn
</code></pre>
<p dir="auto">Or, a java.lang.ClassValue could be used, and the Enum::values method,<br>
defined just once, would root around in reflective stuff once to set up<br>
the CV.  That’s a moral equivalent of condy, but “at a distance”.</p>
<p dir="auto">Or, indy could be used quite directly:</p>
<pre style="margin-left: 15px; margin-right: 15px; padding: 5px; background-color: #F7F7F7; border-radius: 5px 5px 5px 5px; overflow-x: auto; max-width: 90vw;"><code style="margin: 0; border-radius: 3px; background-color: #F7F7F7; padding: 0px;">invokedynamic  {makeValuesArrayBuilder} : ()ThisEnum[]
areturn
</code></pre>
<p dir="auto">I do NOT recommend binding a condy constant to an array.</p>
<p dir="auto">The reason is that such an array is not in a position to be optimized<br>
by the JIT.  Arrays are by default mutable, and they are treated<br>
as constant only in certain “stable” places such as in the body<br>
of a list created by <code style="margin: 0; padding: 0 0.4em; border-radius: 3px; background-color: #F7F7F7;">List::of</code>.  I would accumulate and store<br>
the original source of all clones of the values arrays in a list,<br>
not an array, and make sure the list is immutable (marked stable<br>
in its array state).</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">With that prototype, I was able to generate an enum with ~32K constants.
<br>
Each enum name currently uses two constant pool entries (a Utf8 and String
<br>
entry for the name). Perhaps there's a way to get that down to one entry,
<br>
and get to ~65k enums.</p>
</blockquote></div>
<div class="markdown" style="white-space: normal;">
<p dir="auto">Yes, there is.  Use reflection to infer the strings.  Don’t access them<br>
from the CP via ldc (or as BSM arguments).</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">I collected some preliminary performance data: with 4000 constants, cold
<br>
start time is ~400ms with the new codegen and ~700ms without. With ~32K
<br>
constants, startup takes a full ~17s. From some initial investigation, most
<br>
of that time is spent in Class.getDeclaredFields and
<br>
Unsafe.staticFieldOffset.</p>
</blockquote></div>
<div class="markdown" style="white-space: normal;">
<p dir="auto">The problem with core reflection is that it is kind of bulky and slow.<br>
I was going to suggest short-circuiting through an internal API that<br>
uses MemberName but I see there is not one present.  Funny, I thought<br>
I wrote it for JSR 292, but maybe it was GC-ed somehow.</p>
<p dir="auto">So you may need a sequence of strings to drive the reflective queries,<br>
if you want to avoid all the reflective overheads.  You could derive<br>
a sequence of strings from a single string (or a small number of<br>
max-length strings concatenated) that contains all the field names<br>
in sequence.  That’s O(1) CP entries.  Then you could call the<br>
MemberName query API for single-field queries, mimicing the way it<br>
is used in the Lookup API to build field access method handles.</p>
<p dir="auto">I’m going into these very internal details just to show what would<br>
be required, as a JDK engineer, to build a properly performant<br>
service method that could iterate the fields of an enum and<br>
initialize them properly.</p>
<p dir="auto">For prototypes, core reflect is just fine.  But it will not be as<br>
fast as using the off-label internal APIs such as Unsafe and<br>
MemberName.</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">This has been fun and educational. it also adds complexity to javac, and
<br>
may not be a clear improvement for non-pathological enums that don't have
<br>
many thousands of entries.</p>
<p dir="auto">Do you think it could be worth pursuing?</p>
</blockquote></div>
<div class="markdown" style="white-space: normal;">
<p dir="auto">Actually, no, until there is a compelling use case for more than a few<br>
thousand enum members, but (of course) less than 65K or so.</p>
<p dir="auto">Like you, I found this fun and educational.  It’s a thought exercise<br>
in using metaprogramming (method handle and lookups) instead of bytecode<br>
churning.</p>
<p dir="auto">There are better applications of metaprogramming, such as string concat,<br>
lambda generation, and (I presume) pattern-switch code generation.</p>
<p dir="auto">— John</p>
</div><div class="plaintext" style="white-space: normal;"><blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><p dir="auto">On Wed, Oct 11, 2023 at 6:08 PM John Rose <john.r.rose@oracle.com> wrote:</p>
<blockquote style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; border-left-color: #999999; color: #999999;"><p dir="auto">P.S. I trust it is clear how the single service method below would be used
<br>
in the <clinit> of each client enum. One other thing occurred to me:
<br>
Enums which have bootstrap entanglements with the MethodHandle class would
<br>
need special treatment. For that reason, it might be better to not pass the
<br>
argument MethodHandle enumMemberCreator but rather just call a private
<br>
static method of a fixed name and signature, within the same enum. That can
<br>
be easily done with method handles, if those are “on line”, but can also be
<br>
done with core reflection, which boots up sooner.</p>
<p dir="auto">On 11 Oct 2023, at 18:01, John Rose wrote:</p>
<p dir="auto">```
<br>
public static void initializeEnumClass(Lookup enumClassLU, MethodHandle
<br>
enumMemberCreator) {
<br>
int ordinal = 0;
<br>
if (!enumClassLU.hasPrivateAccess()) throw (IAE);
<br>
Class<? extends Enum> ec =
<br>
enumClassLU.lookupClass().asSubClass(Enum.class);
<br>
for (Field f : ec.getDeclaredFields()) { //order significant here
<br>
if (f is an enum member) {
<br>
Object e = enumMemberCreator.invokeExact(f, ordinal++);
<br>
// next stuff can be done more directly by Unsafe
<br>
assert(f.get(null) == null); //caller resp.
<br>
f.setAccessible(true);
<br>
f.set(null, e);
<br>
}
<br>
}
<br>
}
<br>
```</p>
<p dir="auto">The creation of the values array should be done in `<clinit>`, as well, or
<br>
as a condy (yes, that’s a good usage of condy!) and cloned as a fresh copy
<br>
for each call to `values()`. And it can be done reflectively as well. Just
<br>
iterate over all the fields and store them into the array. (Use the
<br>
`ordinal()` as the array index, or just assert that the fields are in the
<br>
correct order already.)</p>
<p dir="auto">With those two adjustments, to bind enums and build the values array
<br>
reflectively, your enum would be limited only by the maximum size of the
<br>
constant pool. That is, you could have up to about 65k enums (but not the
<br>
whole 2^16).</p>
<br></blockquote></blockquote></div>
<div class="markdown" style="white-space: normal;">

</div></div></body>

</html>