> > * in contrast with the actual opcodes used now, pseudo opcodes don't
>> have a fixed length What I mean here is that "pushconstantint n"
>> maybe resolved to a singlebyte opcode "iconst_1" if n == 1, but to a
>> 2 byte opcode bipush in case n < 128.
> > * the optimization routines will optimize based on these pseudo opcodes
> > * operations with different stack effects need different pseudo opcodes
> > * after all calculations have been done on pseudo opcodes, pseudo
> opcodes are
>> translated into real opcodes
> > * once opcode lengths have been determined, label positions can be
> calculated
>> and used to calculate jump distances.
>
> […]
>>
>> Opinions?
>
> Emitting symbolic opcodes at the end of pass2 certainly seems like the
> more forward looking design strategy to accommodate different strategies
> for actual bytecode resolutions.
Exactly. All that remains is that we design a useable pseudobytecode set now.
Some instructions come to mind quite easily:
* pushconstant (parameters: javatype [:int, :long, ...], value)
* pushregister (parameters: javatype [same], registernumber)
[corresponds to Xloadinstructions]
* pusharrayelement (parameters: javatype) [corresponds to
Xaloadinstructions]
* pushnil
* pusht
* pop (parameters: javatype [because some unboxed types take more
than 1 slot])
* popregister (parameters: javatype [...], registernumber)
[corresponds to Xstoreinstructions]
* poparrayelement (parameters: javatype) [corresponds to Xastore
instructions]
* dup (parameters: none)
* new (parameters: constantpool index)
* newarray (parameters: typeindicator)
* arraylength
* throw
* return (parameters: type)
* invokestatic/invokevirtual/invokespecial (parameters: argument list
types, return type) [possibly under different names]
* compare (parameters: javatype [:int, :long; :ref, ..], condition
[:eq, :ne, :lt, :le, :gt, :ge], resulttype [:boolean, nil])
[corresponds with if_Xcmp instructions], pushes a boolean result on
the stack
* comparewithzero ; same as compare, but uses 1 stack argument
* jumpiftrue (parameters: truelabel) [compares booleanonstack
and jumps to truelabel]
* iinc (parameters: register; constantint)
* instanceof (parameters: classforcheck)
* checkcast (parameters: classforcheck)
* clearvalues (paramerets: none)
* swap
* converttype (parameters: sourcetype, targettype)
* loadconstant (parameters: constantpoolindex) [corresponds to
ldc/ldc_w/ldc2_w instructions]
* getstatic (parameters: static class, static name, javatype)
* putstatic (parameters: static class, static name, javatype)
* goto (parameters: jump target label name)
* label (parameters: label name)
By having compare and jumpiftrue instructions, it's easier to
reorder and integrate the bytecode resulting from compilation of the
IF condition and branching to each of the IF branches.
Some of the other instructions integrate iINSTR, lINSTR, aINSTR. I
decided to do that because each of these instructions have the same
stack and sideeffects. Then, in the optimizer/stack analyser, it's
not necessary to handle them differently. Only in the byte code
generator (resolvers) does it become important what type the resulting
argument will be, so that the right instruction can be chosen.
The instructions above roughly correspond with the instructions used
in our (emit ...) statements. If any instructions are missing, I think
it should be easy to add.
Each pseudo opcode will be created with some data for use for the
codeoptimizer:
* number of stack arguments used (positive == pushed, negative == popped)
* type of side effects (apart from stack effect, which can be ":none"
[in case of dup, iconst_null, getstatic, etc], :locals [in case of
istore_0] or :full [in case side effects can't be completely
determined, for example with invokevirtual/invokestatic])
The side effect information can be used by the bytecode optimizer to
reorder instructions for instructionelimination.
It's just my ideas so far.
Bye,
Erik  who always welcomes your comments but appreciates the fact that
it's new years eve.
