jazzed said...
Printing problem solved. There was a small bug in Hippy's heap implementation. On a 16 bit system with the given implementation one wants all heap pointers to be aligned on even boundaries. Code was there, it was just off by a line [noparse]:)[/noparse]
Apologies for that and the debugging caused. An opportune time though to say I'm still here and following the work you're both doing. Well done, it's moving along quickly. As another hand stirring the soup is going to complicate matters further I'm happy to lurk in the shadows until everything is more stabilised and becomes more unified.
It may be that Hippy's code required to be word aligned (possibly because word[noparse][[/noparse]...] was used),
but we should not rely on that. The reason is that boolean/byte/char arrays are allocated from the heap.
For example·char[noparse]/noparse p = new char[noparse][[/noparse]13] requires 13 bytes + 4 bytes overhead.
As far as I know that size is not incremented to the nearest even number of bytes.
@Jazzed,
You mentioned "build an object based constant table", however I do not think that
is necessary. The only constant pool is the String index table. Scalar constant values
are loaded into variables using code, not a constant pool.
For example: · static char[noparse]/noparse p = new char[noparse]/noparse{'a','b','c','d','e'};
will create a new char array in the heap, and then a loop is used·to fill the array.
That is even so when declared final.
I attached a new javelin ide. I added seperator lines like
[noparse][[/noparse]
Step Into
]
to the status window. This aids in locating what messages were sent
since the last action when using the source level debugger.
@Hippy,
Fortunately it was just a little puzzle requiring little time. I figured you were still lurking here [noparse]:)[/noparse]
Have you ever considered what it might take for opensouce jikes to create propeller bytecode?
That would be a good research canditate for JavaPropII.
@Peter,
Please consider function of LDC, GETFIELD, and PUTFIELD. I could have mis-understood them.
Perhaps I'll experiment with other implementations today.
I attached the jem.out and status.out for the testJVM5.java file.
This is the debug output for a true javelin.
I programmed the javelin using Project->Debug
and then only did Step Into until the while (true) line in main() gets selected.
I think we both need to study this, because for the source level debugger
to work with the prop, the stack frames and heap use must be identical
to the true javelin.
Well it's pretty clear the PC printed is the one straight out of the jem.out file.
Stack and heap appear to be known in advance. Not sure what to make of it.
I'll look at it more tonight.
Jazzed,
Minor update.
Going through the debug files, I realized when QueryStack command is executed, the entire stack
is always returned. So I changed stack[noparse][[/noparse]offset] to stack[noparse][[/noparse]address]. This makes it much
easier to locate stackframes (=fp).
I attached an updated status.out for the example I gave in my previous post.
Also attached the updated javelin ide.
Also note that when QueryHeap command is executed, the returned block sometimes
wraps around $7FFF. This is due to the fixed size (20 bytes) block·that is read.
Obviously the heap starts at $7FFF and only grows downwards.
Another thing I noticed, when a java program ends, the byte at $0000 changes from
$1D (=kDebugMagic) to $ED. Then after clicking reset, it changes to $1D again.
Apparently the value $ED signals end of program, just as the packet $05,$05,$7E does.
Also note that in both heap and stack, big endian is used.
Here are jvmMonitor and jvmMath.
Because some math operations (like division by zero) lead to exceptions,
I added a variable mathException to jvmData, together with API's to
set, clear and test the mathException condition.
Also note that the math methods set the carry if the result is
larger than 16 bits.
Now I will do jvmArray.
regards peter
Post Edited (Peter Verkaik) : 2/13/2008 2:48:18 PM GMT
Hi Peter, tracing status.out is less tedious with jem.out snippets in line ... hope you have a tool to do that [noparse]:)[/noparse]
It seems pretty clear that the JIDE at least has some knowledge of the stack ... question is, are there
queries from JIDE to JVM to get the stack pointer or does the JIDE use stack accounting from the bytecode?
Similar question for heap ... are there queries for the heap pointer from JIDE to JVM ?
If there are any queries, they are not part of the status.out. Please look for this in JIDE.
TIA
kComStatus: this retrieves the current pc, activity and bytecode.
Look for [noparse][[/noparse]QueryStatus] in status.out
kComConfig: this retrieves the device memorysize, programsize and heapbase.
Look for [noparse][[/noparse]QueryConfig] in status.out
Note that the returned programsize is a constant and does not reflect true programsize
(the debugger however calculates it correctly from stackbase).
kComHeap: this retrieves a·block of memory, the command sends startaddress and length,
the jvm returns the current hp and the requested memory bytes.
Look for [noparse][[/noparse]QueryHeap] in status.out
kComStack: this retrieves the current sp, current fp and current mp plus all the stackbytes
from stackbase to current sp.
Look for [noparse][[/noparse]QueryStack] in status.out
So all the values you get are actual values retrieved from the javelin/propjava.
What the IDE does is using the pc value to convert a jemline ( = address of bytecode sequence)
back to the names of a javaclass and method, and the·line in the source file. That's how it is
possible you see the selected line move from file to file while single stepping.
So, the JIDE never asks for a description of the heap other than the initial configuration and these status messages?
Stack is fairly easy to predict. How does the JIDE know what heap addresses to query ?
Until I understand how JIDE knows the addresses to query, I can't help with this.
The debugger collects information based on which static variable is expanded or collapsed.
Variables that are collapsed (+ in front) do not get updated in the debugger screen until expanded by
the user (by clicking on the + in front of the variable, that then changes to a -).
See attached pictures how variables are organized (locals appear under the call stack).
I extracted part of the outputpass() function in file CJEMLinker.cpp in·PropDirect package.
It shows what is exactly outputted after specific bytecodes. Especially lookupswitch
and tableswitch are interesting because the initial padding is left out, but there is padding
at the end to match the·javaclass it is generated from. Not sure why this·is done
in this way. Looks to me the orginal 4byte values would yield the same size, now 2byte values
are used because the javelin only supports 16bit ints.
Anyhow, this gives a detail insight in the jem.out bytecode format.
Peter,
Have you considered how to put the modules you created for the new JVM design together?
There is more work to do in this area of course. It would be neat if we could use a branch
table, rather than a big case.
I'm considering some experiments with the method/class tables with LDC and GET/PUTFIELD
this weekend. I'll search for an alternative to having object references for these operations;
thing is however, the GET/PUTFIELD are almost certainly operations on an object ... possibly
LDC may not be.
I have put in code for all supported bytecodes. I think most code is ok,
some code may be quite wrong, so I would like you to take a look at it.
I inserted comments regarding the JEM format for all bytecodes.
I also made heap management much simpler because no garbage
collection is supported at this time.
Before we move on to integration, I would like the code for the bytecodes
to be completed. There are a few·bytecodes that may need to be revisited.
The math bytecodes for example, I could have misinterpreted the order of
arguments. For comparisons, I did not quite grasp the difference between
ifeq and if_icmpeq for example, as they both branch if two parameters
are equal (or so I understood).
Also not finished are the invokevirtual and invokestatic bytecodes. I did
put two subroutines pushframe() and popframe() in the jvmStack file,
based on the framelayout but it doesn't handle return values yet. So that
needs to be looked at also.
All code uses the API's from jvmData, so if a new global jvm parameter is
required, add it to jvmData and add API's for it, but I think all required parameters
are already·there.
@Jazzed,
I wish there was a ongosub command in spin so we could directly call
a routine based on an index rather than using case but for now I think
we better stick to case to keep code clear rather than optimized.
I've got a good understanding of how objects are created, see new, newarray and anewarray,
and I think that code is ok. If there is anything unclear, please say so and I can try to explain
it better. Also, if you have any specific javelin code that you need true debug info for, please post it
and I will run it on a javelin and generate debug info from that.
I did not quite grasp the difference between ifeq and if_icmpeq for example
Without anything in front of me I recall the first is pop two parameters and compare, while the second is pop one parameter, compare with an immediate operand, both then followed by an address as an immediate operand. - wrong !
The JEM format for both is the bytecode followed by two bytes, which is an offset
to add to the programcounter in case the compare yields true.
The only difference I noticed is that icmpXX pushes either 1 or 0 back onto
the stack depending the compare yielded true or false.
I attached an updated set, I have put in the jvmEngine code (except for native method
handling which requires a return value from doStep).
With all the current code and a JavaProg arraysize of 16KByte, there are still over
1000 longs available.
@Hippy,
ok, so my icmpXX code needs some changing. What about the comparisons?
There was mentioning of pushing back values -1, 0 or 1 if v1<v2, v1==v2 or v1>v2.
Where does that come in? I haven't seen any bytecodes for just comparisons.
Going through the text for invokevirtual and invokestatic, I came up with
the following stack movement (texts are included)
{{ · invokevirtual · Operation: Invoke instance method; dispatch based on class · Format: invokevirtual,indexbyte1,indexbyte2·· · Operand Stack: ...,objectref,[noparse]/noparse]arg1,[noparse][[/noparse]arg2 ... --> ..., · Description · The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the · current class, where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant pool item · at that index must be a symbolic reference to a method, which gives the name and descriptor of the method as · well as a symbolic reference to the class in which the method is to be found. The named method is resolved. · The method must not be an instance initialization method or the class or interface initialization method. · Finally, if the resolved method is protected, and it is either a member of the current class or a member of · a superclass of the current class, then the class of objectref must be either the current class or a subclass · of the current class. · Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure: · If C contains a declaration for an instance method with the same name and descriptor as the resolved method, · and the resolved method is accessible from C, then this is the method to be invoked, and the lookup procedure · terminates. Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the · direct superclass of C ; the method to be invoked is the result of the recursive invocation of this lookup · procedure. Otherwise, an AbstractMethodError is raised. · The objectref must be followed on the operand stack by nargs argument values, where the number, type, and order · of the values must be consistent with the descriptor of the selected instance method. · If the method is synchronized, the monitor associated with objectref is acquired or reentered. · If the method is not native, the nargs argument values and objectref are popped from the operand stack. · A new frame is created on the Java virtual machine stack for the method being invoked. The objectref and the · argument values are consecutively made the values of local variables of the new frame, with objectref in local · variable 0, arg1 in local variable 1 (or, if arg1 is of type long or double, in local variables 1 and 2), · and so on. Any argument value that is of a floating-point type undergoes value set conversion prior to being · stored in a local variable. The new frame is then made current, and the Java virtual machine pc is set to the · opcode of the first instruction of the method to be invoked. Execution continues with the first instruction · of the method. · If the method is native and the platform-dependent code that implements it has not yet been bound into the Java · virtual machine, that is done. The nargs argument values and objectref are popped from the operand stack and are · passed as parameters to the code that implements the method. Any argument value that is of a floating-point type · undergoes value set conversion prior to being passed as a parameter. The parameters are passed and the code is · invoked in an implementation-dependent manner. When the platform-dependent code returns, the following take place: · If the native method is synchronized, the monitor associated with objectref is released or exited as if by · execution of a monitorexit instruction. If the native method returns a value, the return value of the · platform-dependent code is converted in an implementation-dependent way to the return type of the native method · and pushed onto the operand stack. · Runtime Exceptions · If objectref is null, the invokevirtual instruction throws a NullPointerException. · Notes · The nargs argument values and objectref are not one-to-one with the first nargs + 1 local variables. · Argument values of types long and double must be stored in two consecutive local variables, thus more · than nargs local variables may be required to pass nargs argument values to the invoked method.
}}
{{ · invokestatic · Operation: Invoke a class (static) method · Format: invokestatic,indexbyte1,indexbyte2·· · Operand Stack: ...,[noparse]/noparse]arg1,[noparse][[/noparse]arg2 ... --> ..., · Description · The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the · current class, where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant pool item · at that index must be a symbolic reference to a method, which gives the name and descriptor of the method as · well as a symbolic reference to the class in which the method is to be found. The named method is resolved. · The method must not be the class or interface initialization method. It must be static, and therefore cannot · be abstract. · On successful resolution of the method, the class that declared the resolved field is initialized if that class · has not already been initialized. · The operand stack must contain nargs argument values, where the number, type, and order of the values must be · consistent with the descriptor of the resolved method. · If the method is synchronized, the monitor associated with the resolved class is acquired or reentered. · If the method is not native, the nargs argument values are popped from the operand stack. A new frame is · created on the Java virtual machine stack for the method being invoked. The nargs argument values are · consecutively made the values of local variables of the new frame, with arg1 in local variable 0 (or, if arg1 · is of type long or double, in local variables 0 and 1) and so on. Any argument value that is of a floating-point · type undergoes value set conversion prior to being stored in a local variable. The new frame is then made current · and the Java virtual machine pc is set to the opcode of the first instruction of the method to be invoked. · Execution continues with the first instruction of the method. · If the method is native and the platform-dependent code that implements it has not yet been bound into the Java · virtual machine, that is done. The nargs argument values are popped from the operand stack and are passed as · parameters to the code that implements the method. Any argument value that is of a floating-point type undergoes · value set conversion prior to being passed as a parameter. The parameters are passed and the code is invoked in · an implementation-dependent manner. When the platform-dependent code returns, the following take place: · If the native method is synchronized, the monitor associated with the resolved class is released or exited as · if by execution of a monitorexit instruction. If the native method returns a value, the return value of the · platform-dependent code is converted in an implementation-dependent way to the return type of the native method · and pushed onto the operand stack. · Notes · The nargs argument values are not one-to-one with the first nargs local variables. Argument values of types long · and double must be stored in two consecutive local variables, thus more than nargs local variables may be required · to pass nargs argument values to the invoked method.
}}
{{
stackframe just before invoke··· stackframe just after invoke··· stackframe just after return ···· |············ |·················· |············ |················· |············ | ···· +
+·················· +
+ <- fp··········· +
+ ···· | aref / arg0 |·················· | aref / arg0 |················· | return val· | ···· +
+·················· +
+················· +
+ <- sp ···· | arg0 / arg1 |·················· | arg0 / arg1 | ···· |··· .....··· |·················· |··· .....··· | ···· +
+ <- sp············ +
+ ······································ |·· locals··· | ······································ |··· .....··· | ······································ +
+ ······································ | old method· | ······································ +
+ ······································ |·· old fp··· | ······································ +
+ ······································ | return addr | ······································ +
+ <- sp
}}
What it comes down to, is that the aref and arguments appear to be popped off the operand stack
and onto the new frame, but in fact the new frame pointer is adjusted to include the aref and
arguments. As these are removed upon return from the invoked method anyway this should be
no problem.
Comments?
The aref object pointer, the result of new,·is apparently always the first stack entry unless the method is static like main. The method descriptor·to use·(this is not immediately obvious) is the constant table class descriptor start address + index.
The address of the current method descriptor is held in variable mp. The value
of mp is stored in the new frame as old method (and restored upon return), then the new value for mp
is calculated from byte1 and byte2 following the invoke bytecode.
aref is indeed the result of an earlier new bytecode and is only present in case
of invokevirtual. It represents the this identity.
The calculation of mp is not a problem. I just wanted to show that the implementation
can be more simpler than the texts indicate. This is of course possible because the entire
code is statically linked. Everything is resolved except for objects created at runtime.
Therefor we do need to check for null references.
That's where athrow comes in.
· Description · The objectref must be of type reference and must refer to an object that is an instance of class Throwable · or of a subclass of Throwable. It is popped from the operand stack. The objectref is then thrown by searching · the current method for the first exception handler that matches the class of objectref. · If an exception handler that matches objectref is found, it contains the location of the code intended to · handle this exception. The pc register is reset to that location, the operand stack of the current frame is · cleared, objectref is pushed back onto the operand stack, and execution continues. · If no matching exception handler is found in the current frame, that frame is popped. If the current frame · represents an invocation of a synchronized method, the monitor acquired or reentered on invocation of the · method is released or exited (respectively) as if by execution of a monitorexit instruction. Finally, the frame · of its invoker is reinstated, if such a frame exists, and the objectref is rethrown. If no such frame exists, · the current thread exits. · Runtime Exceptions · If objectref is null, athrow throws a NullPointerException instead of objectref. · Notes · The operand stack diagram for the athrow instruction may be misleading: If a handler for this exception is · matched in the current method, the athrow instruction discards all the values on the operand stack, then pushes · the thrown object onto the operand stack. However, if no handler is matched in the current method and the · exception is thrown farther up the method invocation chain, then the operand stack of the method (if any) that · handles the exception is cleared and objectref is pushed onto that empty operand stack. All intervening frames · from the method that threw the exception up to, but not including, the method that handles the exception are · discarded.
}}
From this text I understand all frames are dropped until we reach we a method that has
in its descriptor an exception field that is non-zero.
Do you think that is right?
When I know how to deal with the exceptions, I can complete jvmInvoke and do some
test runs using a simple·java program.
Breakthrough !!
I have the jvm running for invokevirtual, invokestatic and native functions (only message native
function tested). Also working are the arrays, putfield, getfield, putstatic, getstatic, all stack related
bytecodes (after long debugging I discovered the pc was incremented multiple times during stack
movements).
Attached is a picture of a running program. Something is wrong on the last line (s1.indexOf(s2))
so there are still things to debug, but we can print and do math.
Also attached a new IDE, that bugfixes some debug messages like heap[noparse][[/noparse]$xxxx] where $xxxx
was sometimes negative or beyond memorysize.
At the moment the native functions are called from jvmInvoke which is fine for now.
The current spin program size is 6967 longs (that includes the·$4000 bytes for java program)
and there are still 1221 longs free.
Good work Peter. I'll look the design in more detail.
I'm curious to see how the Native stuff turns out. I did provide a stack based interface
you might consider using and is connected between main and the jvm in my last zip post.
The basic interface flow is: The parameter "push" point is in call method. The parameter
"pop" point is in the Native call's parameters which seems reasonable since the type of
data is prearranged by native id. A return code is pushed by the native wrapper as
required, and finally the jvm return method pops the result.
Stack interface already implemented [noparse]:)[/noparse]
Peter I was able to integrate your package with my text based debugger
with only one change in jvmEngine.spin doByte to PUB. Very flexible [noparse]:)[/noparse]
Having all those files make compile long, but only annoying for F8 build.
Comments
Apologies for that and the debugging caused. An opportune time though to say I'm still here and following the work you're both doing. Well done, it's moving along quickly. As another hand stirring the soup is going to complicate matters further I'm happy to lurk in the shadows until everything is more stabilised and becomes more unified.
but we should not rely on that. The reason is that boolean/byte/char arrays are allocated from the heap.
For example·char[noparse]/noparse p = new char[noparse][[/noparse]13] requires 13 bytes + 4 bytes overhead.
As far as I know that size is not incremented to the nearest even number of bytes.
@Jazzed,
You mentioned "build an object based constant table", however I do not think that
is necessary. The only constant pool is the String index table. Scalar constant values
are loaded into variables using code, not a constant pool.
For example:
· static char[noparse]/noparse p = new char[noparse]/noparse{'a','b','c','d','e'};
will create a new char array in the heap, and then a loop is used·to fill the array.
That is even so when declared final.
I attached a new javelin ide. I added seperator lines like
[noparse][[/noparse]
Step Into
]
to the status window. This aids in locating what messages were sent
since the last action when using the source level debugger.
regards peter
Fortunately it was just a little puzzle requiring little time. I figured you were still lurking here [noparse]:)[/noparse]
Have you ever considered what it might take for opensouce jikes to create propeller bytecode?
That would be a good research canditate for JavaPropII.
@Peter,
Please consider function of LDC, GETFIELD, and PUTFIELD. I could have mis-understood them.
Perhaps I'll experiment with other implementations today.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--jazzed·... about·living in ·http://en.wikipedia.org/wiki/Silicon_Valley
I attached the jem.out and status.out for the testJVM5.java file.
This is the debug output for a true javelin.
I programmed the javelin using Project->Debug
and then only did Step Into until the while (true) line in main() gets selected.
I think we both need to study this, because for the source level debugger
to work with the prop, the stack frames and heap use must be identical
to the true javelin.
regards peter
Stack and heap appear to be known in advance. Not sure what to make of it.
I'll look at it more tonight.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--jazzed·... about·living in ·http://en.wikipedia.org/wiki/Silicon_Valley
Minor update.
Going through the debug files, I realized when QueryStack command is executed, the entire stack
is always returned. So I changed stack[noparse][[/noparse]offset] to stack[noparse][[/noparse]address]. This makes it much
easier to locate stackframes (=fp).
I attached an updated status.out for the example I gave in my previous post.
Also attached the updated javelin ide.
Also note that when QueryHeap command is executed, the returned block sometimes
wraps around $7FFF. This is due to the fixed size (20 bytes) block·that is read.
Obviously the heap starts at $7FFF and only grows downwards.
Another thing I noticed, when a java program ends, the byte at $0000 changes from
$1D (=kDebugMagic) to $ED. Then after clicking reset, it changes to $1D again.
Apparently the value $ED signals end of program, just as the packet $05,$05,$7E does.
Also note that in both heap and stack, big endian is used.
regards peter
Note that unsupported bytecodes are commented out.
I also edited status.out by including parts of jem.out so it easier
to follow what is happening. I added comments to identify recognized values.
Now I will do jvmMonitor and jvmMath.
regards peter
Because some math operations (like division by zero) lead to exceptions,
I added a variable mathException to jvmData, together with API's to
set, clear and test the mathException condition.
Also note that the math methods set the carry if the result is
larger than 16 bits.
Now I will do jvmArray.
regards peter
Post Edited (Peter Verkaik) : 2/13/2008 2:48:18 PM GMT
It seems pretty clear that the JIDE at least has some knowledge of the stack ... question is, are there
queries from JIDE to JVM to get the stack pointer or does the JIDE use stack accounting from the bytecode?
Similar question for heap ... are there queries for the heap pointer from JIDE to JVM ?
If there are any queries, they are not part of the status.out. Please look for this in JIDE.
TIA
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--jazzed·... about·living in ·http://en.wikipedia.org/wiki/Silicon_Valley
kComStatus: this retrieves the current pc, activity and bytecode.
Look for [noparse][[/noparse]QueryStatus] in status.out
kComConfig: this retrieves the device memorysize, programsize and heapbase.
Look for [noparse][[/noparse]QueryConfig] in status.out
Note that the returned programsize is a constant and does not reflect true programsize
(the debugger however calculates it correctly from stackbase).
kComHeap: this retrieves a·block of memory, the command sends startaddress and length,
the jvm returns the current hp and the requested memory bytes.
Look for [noparse][[/noparse]QueryHeap] in status.out
kComStack: this retrieves the current sp, current fp and current mp plus all the stackbytes
from stackbase to current sp.
Look for [noparse][[/noparse]QueryStack] in status.out
So all the values you get are actual values retrieved from the javelin/propjava.
What the IDE does is using the pc value to convert a jemline ( = address of bytecode sequence)
back to the names of a javaclass and method, and the·line in the source file. That's how it is
possible you see the selected line move from file to file while single stepping.
regards peter
Stack is fairly easy to predict. How does the JIDE know what heap addresses to query ?
Until I understand how JIDE knows the addresses to query, I can't help with this.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Variables that are collapsed (+ in front) do not get updated in the debugger screen until expanded by
the user (by clicking on the + in front of the variable, that then changes to a -).
See attached pictures how variables are organized (locals appear under the call stack).
regards peter
It shows what is exactly outputted after specific bytecodes. Especially lookupswitch
and tableswitch are interesting because the initial padding is left out, but there is padding
at the end to match the·javaclass it is generated from. Not sure why this·is done
in this way. Looks to me the orginal 4byte values would yield the same size, now 2byte values
are used because the javelin only supports 16bit ints.
Anyhow, this gives a detail insight in the jem.out bytecode format.
regards peter
Have you considered how to put the modules you created for the new JVM design together?
There is more work to do in this area of course. It would be neat if we could use a branch
table, rather than a big case.
I'm considering some experiments with the method/class tables with LDC and GET/PUTFIELD
this weekend. I'll search for an alternative to having object references for these operations;
thing is however, the GET/PUTFIELD are almost certainly operations on an object ... possibly
LDC may not be.
Later ....
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
I have put in code for all supported bytecodes. I think most code is ok,
some code may be quite wrong, so I would like you to take a look at it.
I inserted comments regarding the JEM format for all bytecodes.
I also made heap management much simpler because no garbage
collection is supported at this time.
Before we move on to integration, I would like the code for the bytecodes
to be completed. There are a few·bytecodes that may need to be revisited.
The math bytecodes for example, I could have misinterpreted the order of
arguments. For comparisons, I did not quite grasp the difference between
ifeq and if_icmpeq for example, as they both branch if two parameters
are equal (or so I understood).
Also not finished are the invokevirtual and invokestatic bytecodes. I did
put two subroutines pushframe() and popframe() in the jvmStack file,
based on the framelayout but it doesn't handle return values yet. So that
needs to be looked at also.
All code uses the API's from jvmData, so if a new global jvm parameter is
required, add it to jvmData and add API's for it, but I think all required parameters
are already·there.
@Jazzed,
I wish there was a ongosub command in spin so we could directly call
a routine based on an index rather than using case but for now I think
we better stick to case to keep code clear rather than optimized.
I've got a good understanding of how objects are created, see new, newarray and anewarray,
and I think that code is ok. If there is anything unclear, please say so and I can try to explain
it better. Also, if you have any specific javelin code that you need true debug info for, please post it
and I will run it on a javelin and generate debug info from that.
regards peter
I did not quite grasp the difference between ifeq and if_icmpeq for example
Without anything in front of me I recall the first is pop two parameters and compare, while the second is pop one parameter, compare with an immediate operand, both then followed by an address as an immediate operand. - wrong !
Post Edited (hippy) : 2/16/2008 2:15:36 PM GMT
to add to the programcounter in case the compare yields true.
The only difference I noticed is that icmpXX pushes either 1 or 0 back onto
the stack depending the compare yielded true or false.
I attached an updated set, I have put in the jvmEngine code (except for native method
handling which requires a return value from doStep).
With all the current code and a JavaProg arraysize of 16KByte, there are still over
1000 longs available.
regards peter
ok, so my icmpXX code needs some changing. What about the comparisons?
There was mentioning of pushing back values -1, 0 or 1 if v1<v2, v1==v2 or v1>v2.
Where does that come in? I haven't seen any bytecodes for just comparisons.
regards peter
earlier link given in this thread, I googled and found:
http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc.html
These at least provide all info, including stack movements.
Attached is the updated jvmCompareJump file.
regards peter
Post Edited (Peter Verkaik) : 2/16/2008 4:51:39 PM GMT
the following stack movement (texts are included)
{{
· invokevirtual
· Operation: Invoke instance method; dispatch based on class
· Format: invokevirtual,indexbyte1,indexbyte2··
· Operand Stack: ...,objectref,[noparse]/noparse]arg1,[noparse][[/noparse]arg2 ... --> ...,
· Description
· The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the
· current class, where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant pool item
· at that index must be a symbolic reference to a method, which gives the name and descriptor of the method as
· well as a symbolic reference to the class in which the method is to be found. The named method is resolved.
· The method must not be an instance initialization method or the class or interface initialization method.
· Finally, if the resolved method is protected, and it is either a member of the current class or a member of
· a superclass of the current class, then the class of objectref must be either the current class or a subclass
· of the current class.
· Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:
· If C contains a declaration for an instance method with the same name and descriptor as the resolved method,
· and the resolved method is accessible from C, then this is the method to be invoked, and the lookup procedure
· terminates. Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the
· direct superclass of C ; the method to be invoked is the result of the recursive invocation of this lookup
· procedure. Otherwise, an AbstractMethodError is raised.
· The objectref must be followed on the operand stack by nargs argument values, where the number, type, and order
· of the values must be consistent with the descriptor of the selected instance method.
· If the method is synchronized, the monitor associated with objectref is acquired or reentered.
· If the method is not native, the nargs argument values and objectref are popped from the operand stack.
· A new frame is created on the Java virtual machine stack for the method being invoked. The objectref and the
· argument values are consecutively made the values of local variables of the new frame, with objectref in local
· variable 0, arg1 in local variable 1 (or, if arg1 is of type long or double, in local variables 1 and 2),
· and so on. Any argument value that is of a floating-point type undergoes value set conversion prior to being
· stored in a local variable. The new frame is then made current, and the Java virtual machine pc is set to the
· opcode of the first instruction of the method to be invoked. Execution continues with the first instruction
· of the method.
· If the method is native and the platform-dependent code that implements it has not yet been bound into the Java
· virtual machine, that is done. The nargs argument values and objectref are popped from the operand stack and are
· passed as parameters to the code that implements the method. Any argument value that is of a floating-point type
· undergoes value set conversion prior to being passed as a parameter. The parameters are passed and the code is
· invoked in an implementation-dependent manner. When the platform-dependent code returns, the following take place:
· If the native method is synchronized, the monitor associated with objectref is released or exited as if by
· execution of a monitorexit instruction. If the native method returns a value, the return value of the
· platform-dependent code is converted in an implementation-dependent way to the return type of the native method
· and pushed onto the operand stack.
· Runtime Exceptions
· If objectref is null, the invokevirtual instruction throws a NullPointerException.
· Notes
· The nargs argument values and objectref are not one-to-one with the first nargs + 1 local variables.
· Argument values of types long and double must be stored in two consecutive local variables, thus more
· than nargs local variables may be required to pass nargs argument values to the invoked method.
}}
{{
· invokestatic
· Operation: Invoke a class (static) method
· Format: invokestatic,indexbyte1,indexbyte2··
· Operand Stack: ...,[noparse]/noparse]arg1,[noparse][[/noparse]arg2 ... --> ...,
· Description
· The unsigned indexbyte1 and indexbyte2 are used to construct an index into the runtime constant pool of the
· current class, where the value of the index is (indexbyte1 << 8) | indexbyte2. The runtime constant pool item
· at that index must be a symbolic reference to a method, which gives the name and descriptor of the method as
· well as a symbolic reference to the class in which the method is to be found. The named method is resolved.
· The method must not be the class or interface initialization method. It must be static, and therefore cannot
· be abstract.
· On successful resolution of the method, the class that declared the resolved field is initialized if that class
· has not already been initialized.
· The operand stack must contain nargs argument values, where the number, type, and order of the values must be
· consistent with the descriptor of the resolved method.
· If the method is synchronized, the monitor associated with the resolved class is acquired or reentered.
· If the method is not native, the nargs argument values are popped from the operand stack. A new frame is
· created on the Java virtual machine stack for the method being invoked. The nargs argument values are
· consecutively made the values of local variables of the new frame, with arg1 in local variable 0 (or, if arg1
· is of type long or double, in local variables 0 and 1) and so on. Any argument value that is of a floating-point
· type undergoes value set conversion prior to being stored in a local variable. The new frame is then made current
· and the Java virtual machine pc is set to the opcode of the first instruction of the method to be invoked.
· Execution continues with the first instruction of the method.
· If the method is native and the platform-dependent code that implements it has not yet been bound into the Java
· virtual machine, that is done. The nargs argument values are popped from the operand stack and are passed as
· parameters to the code that implements the method. Any argument value that is of a floating-point type undergoes
· value set conversion prior to being passed as a parameter. The parameters are passed and the code is invoked in
· an implementation-dependent manner. When the platform-dependent code returns, the following take place:
· If the native method is synchronized, the monitor associated with the resolved class is released or exited as
· if by execution of a monitorexit instruction. If the native method returns a value, the return value of the
· platform-dependent code is converted in an implementation-dependent way to the return type of the native method
· and pushed onto the operand stack.
· Notes
· The nargs argument values are not one-to-one with the first nargs local variables. Argument values of types long
· and double must be stored in two consecutive local variables, thus more than nargs local variables may be required
· to pass nargs argument values to the invoked method.
}}
{{
stackframe just before invoke··· stackframe just after invoke··· stackframe just after return
···· |············ |·················· |············ |················· |············ |
···· +
+·················· +
+ <- fp··········· +
+
···· | aref / arg0 |·················· | aref / arg0 |················· | return val· |
···· +
+·················· +
+················· +
+ <- sp
···· | arg0 / arg1 |·················· | arg0 / arg1 |
···· |··· .....··· |·················· |··· .....··· |
···· +
+ <- sp············ +
+
······································ |·· locals··· |
······································ |··· .....··· |
······································ +
+
······································ | old method· |
······································ +
+
······································ |·· old fp··· |
······································ +
+
······································ | return addr |
······································ +
+ <- sp
}}
What it comes down to, is that the aref and arguments appear to be popped off the operand stack
and onto the new frame, but in fact the new frame pointer is adjusted to include the aref and
arguments. As these are removed upon return from the invoked method anyway this should be
no problem.
Comments?
regards peter
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
of mp is stored in the new frame as old method (and restored upon return), then the new value for mp
is calculated from byte1 and byte2 following the invoke bytecode.
aref is indeed the result of an earlier new bytecode and is only present in case
of invokevirtual. It represents the this identity.
regards peter
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
can be more simpler than the texts indicate. This is of course possible because the entire
code is statically linked. Everything is resolved except for objects created at runtime.
Therefor we do need to check for null references.
That's where athrow comes in.
{{
· athrow
· Operation: Throw exception or error
· Format: athrow··
· Operand Stack: ...,objectref --> objectref
· Description
· The objectref must be of type reference and must refer to an object that is an instance of class Throwable
· or of a subclass of Throwable. It is popped from the operand stack. The objectref is then thrown by searching
· the current method for the first exception handler that matches the class of objectref.
· If an exception handler that matches objectref is found, it contains the location of the code intended to
· handle this exception. The pc register is reset to that location, the operand stack of the current frame is
· cleared, objectref is pushed back onto the operand stack, and execution continues.
· If no matching exception handler is found in the current frame, that frame is popped. If the current frame
· represents an invocation of a synchronized method, the monitor acquired or reentered on invocation of the
· method is released or exited (respectively) as if by execution of a monitorexit instruction. Finally, the frame
· of its invoker is reinstated, if such a frame exists, and the objectref is rethrown. If no such frame exists,
· the current thread exits.
· Runtime Exceptions
· If objectref is null, athrow throws a NullPointerException instead of objectref.
· Notes
· The operand stack diagram for the athrow instruction may be misleading: If a handler for this exception is
· matched in the current method, the athrow instruction discards all the values on the operand stack, then pushes
· the thrown object onto the operand stack. However, if no handler is matched in the current method and the
· exception is thrown farther up the method invocation chain, then the operand stack of the method (if any) that
· handles the exception is cleared and objectref is pushed onto that empty operand stack. All intervening frames
· from the method that threw the exception up to, but not including, the method that handles the exception are
· discarded.
}}
From this text I understand all frames are dropped until we reach we a method that has
in its descriptor an exception field that is non-zero.
Do you think that is right?
When I know how to deal with the exceptions, I can complete jvmInvoke and do some
test runs using a simple·java program.
regards peter
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
It's nice to see work progressing and I am waiting with baited breath to see what you guys come up with next.
@jazzed,
Just drop me a message and I'll what (if anything) I can do when you think that the platform is ready
In the meantime, I'm going to get me a prop clip and start trying some of this out.
~ Christopher
I have the jvm running for invokevirtual, invokestatic and native functions (only message native
function tested). Also working are the arrays, putfield, getfield, putstatic, getstatic, all stack related
bytecodes (after long debugging I discovered the pc was incremented multiple times during stack
movements).
Attached is a picture of a running program. Something is wrong on the last line (s1.indexOf(s2))
so there are still things to debug, but we can print and do math.
Also attached a new IDE, that bugfixes some debug messages like heap[noparse][[/noparse]$xxxx] where $xxxx
was sometimes negative or beyond memorysize.
At the moment the native functions are called from jvmInvoke which is fine for now.
The current spin program size is 6967 longs (that includes the·$4000 bytes for java program)
and there are still 1221 longs free.
regards peter
I'm curious to see how the Native stuff turns out. I did provide a stack based interface
you might consider using and is connected between main and the jvm in my last zip post.
The basic interface flow is: The parameter "push" point is in call method. The parameter
"pop" point is in the Native call's parameters which seems reasonable since the type of
data is prearranged by native id. A return code is pushed by the native wrapper as
required, and finally the jvm return method pops the result.
Stack interface already implemented [noparse]:)[/noparse]
Later.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)
Post Edited (jazzed) : 2/21/2008 5:20:23 AM GMT
with only one change in jvmEngine.spin doByte to PUB. Very flexible [noparse]:)[/noparse]
Having all those files make compile long, but only annoying for F8 build.
Good work.
Time for test suites ?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
jazzed·... about·living in·http://en.wikipedia.org/wiki/Silicon_Valley
Traffic is slow at times, but Parallax orders·always get here fast 8)