<div dir="ltr"><div dir="ltr">Am So., 7. Aug. 2022 um 17:46 Uhr schrieb Brian Goetz <<a href="mailto:brian.goetz@oracle.com">brian.goetz@oracle.com</a>>:</div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<blockquote type="cite">
<div dir="ltr">### Lower than I like<br>
<br>
The bulk of API usage stays on a single level of abstraction,
for<br>
example working with *Desc entry points instead of *Entry.
There are<br>
rare places where this slipped at little.<br>
</div>
</blockquote>
<br>
I'm not following what you're getting at here. Do you mean there
are gaps where we are not consistent about choices of Desc vs
Entry? Or simply that you stuck to the Desc level as a user? And
if so, was there friction here?<br></div></blockquote><div><br></div><div>This was intended as a comment on a common theme of the following four points,</div><div>where a small gap in the API or in my understanding forced me slightly out of the</div><div>lane in which I would have preferred to stay. As it is, I find Classfile highly regular</div><div>and predictable, even in this early state. The one exception that comes to mind</div><div>is two New*Array instructions not having a ClassDesc-based of() factory.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<blockquote type="cite">
<div dir="ltr">The one place where I use labelToBci() is
try/catch/finally. There is<br>
the special case of exceptionCatch() failing for an empty
region, a<br>
condition that in turn can lead to handler blocks becoming<br>
unreachable. For me, the only robust way to deal with this to
a)<br>
guard against an empty region by inspecting the bcis and b)<br>
subsequently omitting the invalid/unreachable parts.<br>
</div>
</blockquote>
<br>
This raises a good question about how much the library wants to do
to "fix" questionable bytecode. We already NOP out unreachable
bytecode (otherwise the verifier freaks out). Should we just
silently drop catch clauses associated with empty try blocks? (We
won't know that they are empty until after all the labels are
resolved, so we can't usually detect this at the point of emitting
the catch entry.) What about when, by the time we get to the end of
generation, a label used in a try-catch, or LVT[T], isn't bound?
Should we throw, or just drop the entry? I suspect one size does
not fit all here and we have to design some more options-handling. </div></blockquote><div><br></div><div>I've currently disabled NOP-ing because I want to know about unreachable code early.</div><div>If I find a situation where I cannot prevent such code with reasonable effort, I will have</div><div>to revert to the default behaviour. exceptionCatch() on an empty region is an example</div><div>for a situation that I cannot detect with reasonable effort upfront, and the default of</div><div>throwing helped me to think through what is happening there and about the potential</div><div>consequences.</div><div><br></div><div>From my limited experience with ifThenElse(), the higher level block-based entry </div><div>points are in a better position to produce semantically equivalent code ("do what I</div><div>mean") instead of just passing through an instruction stream verbatim ("do as I say"). </div><div>This probably requires exclusive control over both sides of the involved labels'</div><div>contract, position marking and position targeting/consumption.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<blockquote type="cite">
<div dir="ltr">Another single use only is constantPool(), to go
from a DMHD instance<br>
to CodeBuilder's (field|invoke)Instruction. This was a
consequence of<br>
DMHD only providing the lookupDescriptor() as String and not as
an MTD<br>
as well. With hindsight, it may have been better for me to
recover<br>
the MTD from the String regardless, and to stay on the level of
*Desc<br>
throughout.<br>
</div>
</blockquote>
<br>
Is there something missing that would bridge that for you?<br></div></blockquote><div><br></div><div>I have many uses of DMHD: handover points to the runtime are stored as DMHD,</div><div>Java interop goes from Member to DMHD and then onward, every function arity</div><div>gets eventually assigned one or two DMHD. The need to generate a field or</div><div>invoke instruction from DMHD-like data arises quite often, and a CodeBuilder</div><div>method to facilitate this from DMHD components feels kind of natural. This needs</div><div>a mapping from DMHD$Kind to Opcode, and DMHD exposing its lookup as MTD</div><div>would allow to keep this on the *Desc level.</div><div><br></div><div>Right now I am using these two function variants:</div><div><br></div><div><font face="monospace">(defn invoke<br> (^CodeBuilder [^CodeBuilder xb ^DirectMethodHandleDesc$Kind kind<br> ^ClassDesc owner ^String method-name<br> ^String lookup-descriptor ^boolean owner-interface?]<br> (let [cp (.constantPool xb)<br> owner (.classEntry cp owner)<br> nm (.utf8Entry cp method-name)<br> tp (.utf8Entry cp lookup-descriptor)<br> nat (.natEntry cp nm tp)<br> opc (case (.refKind kind)<br> #_REF_getField 1 Opcode/GETFIELD<br> #_REF_getStatic 2 Opcode/GETSTATIC<br> #_REF_putField 3 Opcode/PUTFIELD<br> #_REF_putStatic 4 Opcode/PUTSTATIC<br> #_REF_invokeVirtual 5 Opcode/INVOKEVIRTUAL<br> #_REF_invokeStatic 6 Opcode/INVOKESTATIC<br> #_REF_invokeSpecial 7 Opcode/INVOKESPECIAL<br> #_REF_newInvokeSpecial 8 Opcode/INVOKESPECIAL<br> #_REF_invokeInterface 9 Opcode/INVOKEINTERFACE)]<br> (if (< (.refKind kind) #_REF_invokeVirtual 5)<br> (.fieldInstruction xb opc (.fieldRefEntry cp owner nat))<br> (.invokeInstruction xb opc (if owner-interface?<br> (.interfaceMethodRefEntry cp owner nat)<br> (.methodRefEntry cp owner nat))))))<br> (^CodeBuilder [^CodeBuilder xb ^DirectMethodHandleDesc mhd]<br> (invoke xb (.kind mhd) (.owner mhd) (.methodName mhd) (.lookupDescriptor mhd)<br> (.isOwnerInterface mhd))))</font><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<br>
<blockquote type="cite">
<div dir="ltr">Finally, is there a way to decide between
tableswitch and<br>
lookupswitch? Lacking something better, I'm trying to emulate
this<br>
code here:<br>
<a href="https://github.com/openjdk/jdk-sandbox/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java#L1320" target="_blank">https://github.com/openjdk/jdk-sandbox/blob/master/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java#L1320</a><br>
</div>
</blockquote>
<br>
Most compilers use a heuristic based on the size of the two
alternatives, comparing `(hi-lo) / count` to some density
threshold. You're mostly optimizing for bytecode size here, since
when the JIT gets its hands on it, it has its own heuristics.<br></div></blockquote><div><br></div><div>Understood. It would be nice if Classfile would offer some "blessed" or just reasonable heuristic here.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
<br>
<blockquote type="cite">
<div dir="ltr">### Lost in translation<br>
<br>
One feature I cannot duplicate with Classfile is
try/catch/finally in<br>
expression position when the operand stack is not empty. The
old<br>
bytecode generator dealt with this case by unwinding the operand
stack<br>
into locals, evaluating the t/c/f, and then rebuilding the
operand<br>
stack with the result on top. But to do this, one needs to know
what<br>
the operand stack looks like at the point of the `try`.<br>
</div>
</blockquote>
<br>
Interesting point. We do not build stack maps as we go, so we don't
have our hands readily on the stack state. However, I could imagine
an overload of the try-catch builder that would let you feed it a
TypeKind[], and that would use allocateLocal to automate the
push/pop logic. This is something you should be able to build from
outside the library, too; this would be a good experiment try try.
(You'd have to manually compute the stack state.)<br></div></blockquote><div><br></div><div>I'm currently experimenting with minimal stack tracking, basically an approximated</div><div>flag "no stack operands" passed down during parsing. If a try is reached without</div><div>this flag being present, it is wrapped in a no-argument closure and called. My</div><div>hope is that this is much easier to get right than accurate stack tracking.</div><div><br></div><div>--mva</div><div><br></div><div><br></div></div></div>