The Uva Virtual Machine

3 downloads 0 Views 268KB Size Report
Java virtual machine but has improved support for updatability, evolution and flexibility. ..... invocation count on the TS, and the initial values of its arguments on the DS. ..... The class must contain a static, public chain with the signature main(#[string). ... int i (used only in Uva) float f (used only in Uva). Reference data types:.
The Uva Virtual Machine Dan Österberg Johan Lilius TUCS Turku Centre for Computer Science Department of Computer Science, Åbo Akademi University Lemminkäisenkatu 14, FIN-20520 Turku, Finland

Turku Centre for Computer Science TUCS Technical Report No 551 September 2003 ISBN 952-12-1214-4 ISSN 1239-1891

Abstract This paper describes a virtual machine which is largely compatible with a Java virtual machine but has improved support for updatability, evolution and flexibility. This paper gives a detailed, but not full, description of the virtual machine and its assembler programming language. The foundation for these specifications is a number of concepts that potentially seriously improve both dynamic and static updatability. These unique concepts are introduced elsewhere, but their practical usage is to some degree demonstrated. The specifications in this paper could form a basis for approaches that rival Java and outperform it in evolvability and updatability.

Keywords: runtime updating, dynamic reconfiguration, dynamic code replacement, on-the-fly program modification, on-line software version change, software hot swapping, programming languages, virtual machines, object-orientation, updatability

TUCS Laboratory Embedded Systems Laboratory

Contents 1 Introduction

1

2 The Uva Virtual Machine as a Stack Machine 2 2.1 Stack Machines...................................................................................2 2.2 UVM Stacks........................................................................................3 3 UVM Concepts 6 3.1 Components........................................................................................6 3.2 Behavior..............................................................................................8 3.3 Updating..............................................................................................9 3.4 Descriptors and Type Comparison....................................................10 3.5 Execution..........................................................................................11 3.5.1 Resolving a Class......................................................................11 3.5.2 Invoking a Chain.......................................................................11 3.5.3 Running an Application............................................................12 4 The Uva Class File 13 4.1 Constants...........................................................................................13 4.2 Class File Format..............................................................................14 5 Uva Bytecode 20 5.1 Standard Opcodes.............................................................................20 5.2 Reserved Opcodes.............................................................................37 5.3 Quick Opcodes..................................................................................38 6 The Portable Virtual Machine 42 6.1 PVM History and Specifications......................................................42 6.2 Extensions and Modifications...........................................................42 6.3 Supporting Uva.................................................................................44 7 Programming in Uva 45 7.1 Reserved Words................................................................................45 7.2 Uva Source Files...............................................................................49 7.3 Uva Assembler Code........................................................................51 7.4 Uva Compiler....................................................................................53 8 Conclusion and Future Work

54

A Appendix A 55 A.1 Fibonacci Example...........................................................................55 A.2 Draw Example..................................................................................60 References

66

1 Introduction The environment Uva (Updatable Virtual Architecture) is introduced in [Öst03] and contains a source language, assembler bytecode and a virtual machine. The initial design goal set for Uva is an environment that better supports dynamic updating. This would allow applications to be patched and upgraded safely while kept running. Update distribution and maintenance would benefit from such a capability. But Uva enables more than dynamic updating. Static updating and evolution flexibility is also greatly improved, reducing the workload required for application evolution, and enabling large modifications to the design of applications. To sum up, application development in Uva could be more time and cost efficient than in traditional languages, and new applications areas could also be explored. In order to understand the design of Uva, readers are strongly recommended to first read [Öst03]. Uva is designed based on several unique concepts for improved updatability, all of which are explained in that paper. Of these concepts, EOP (EntityOriented Programing), the Sequence Model, recursive returns and features could be mentioned. The paper ends with an overview of the whole Uva environment. This technical report specifies the inner components and functionality of the UVM (Uva Virtual Machine). Most issues are specified in adequate detail, but, some less important details might be left out, and should then be considered to be similar to the corresponding details in a JVM (Java Virtual Machine). The reader is hence assumed to have some knowledge of how a JVM works. Enough information is also provided in order to make the reader able to write Uva assembler code, which is a cross between the USL (Uva Source Language) and the bytecode for the UVM.

1

2 The Uva Virtual Machine as a Stack Machine Uva has a very specific execution model, and is therefore best suited for interpreted execution. Not only for the sake of efficiency, but also in order to make execution more updatable and sequence model [Öst03] look-alike, Uva source code is not interpreted directly. In accordance with the design of Java, two levels of code are provided; Uva source code and Uva bytecode. Uva bytecode is a binary, pre-compiled version of the source code, and run by the UVM. The virtual machine specification is in practice what makes the applications more updatable, so source code syntax and rules only serve the purpose of providing a more user-friendly view of UVM bytecode and behavior. 2.1 Stack Machines Many, or perhaps even most, approaches to virtual machine design - including Java, Forth and Python - are stack based. Stack based architectures are thoroughly discussed in [Koo89], and some of the key issues are described below. The programming language Forth is the forefather of all stack machines - both physical and virtual. It was invented by Charles Moore; first specified as a programming environment in the 1960's, and later formalized as a programming language in 1977. It was intended for real-time control over astronomical equipment, and also proved useful for AI programming. Like Java, it uses a virtual stack machine, but is interactive and based on incremental program extension. In Forth, subroutines are called words and are very short. They basically only contain some combination of (un)conditional calls to other words. This results in heavy use of subroutine calls, which led to the innovation of stack machines. In a study of Forth applications, Koopman found that 12.2 % of all instructions executed, and 26.9 % of all instructions compiled were subroutine calls [Koo89]. Such high frequencies of subroutine calls are not a problem, because stack machines typically have very low overhead for calling subroutines. This enReturn stack Data stack courages and supports wripush PC operands ting short subroutines, which in turn improves updatability pop PC and suits the sequence model next PC PC ALU well. Since the stack machioperation Instruction result nes we will work with are decoding virtual and not physical stack instruction machines, this might not be data Memory instruction address entirely true in the end. But even so, inlining and other Illustration 1: The components in a typical (mutiple-)stack optimizations may be used to machine. I/O has been left out, and control logic is only indirectly present through the communication links visualized with arrows. improve the situation. To improve performance, a "top-of-stack" register may be placed Forth - as well as other between the ALU and the data stack. Execution typically goes stack machines - calculates something like this: The PC is used to fetch an instruction from expressions according to the memory. This instruction is decoded and the ALU invoked to Reverse Polish Notation perform the operation needed to execute it. The ALU pops operands from the stack and reads data from memory in order to (RPN), which is more com- produce a result that is pushed back onto the stack. The PC is monly known as postfix nota- also updated, and the next instruction can then be fetched. tion. This means that all ope2

rands precedes the operator, and is a natural notation for any computer. (Register machines would first load the operands to registers and then perform the operation.) Multiple-stack machines are commonly preferred over their single-stack cousins. Typically, two stacks are used; one for storing return addresses, and one for storing arguments and intermediate values. This provides a separation between control flow and data, and also eliminates the need for parameter copying. Although Java does not explicitly declare to use a return stack, its use of stack frames enables virtual machines to act as if providing a separate return and data stack. A pure stack machine uses 0operand addressing, which means that operands are taken from the stack instead of from the instruction itself. A JVM is to be considered a pure stack machine. Most constants must still be included with the instruction, but the overall design is anyhow much simplified. For comparison, a 2-operand stack machine is not a true stack machine, but virtually only a general-purpose register machine. it would support instructions like mov ax, [addr1]; add ax, [addr2] (actually being two 80x86 assembler instruction [KK94]), while a pure stack machine would perform the same action using push [addr1]; push [addr2]; add. At first thought, this example might seem to suggest that a pure stack machine is less efficient because it requires more instructions to produce the same result. But as a matter of fact, many experts would argue that the opposite is true. Pure stack machines have many practical advantages, such as faster instruction decoding, faster instruction execution, elimination of the need for pipelining, a reduced instruction set, simplicity and more. Compared to register machines, stack machines tend to have very compact code [Koo89]. One reason is the simplicity and lack of operands for many instructions in stack machines. In the example mentioned in the previous paragraph, both register machine instructions not only contained an opcode and two operands, but also information about the opcode formats – being constants, registers or addresses. In addition, values that have recently been calculated or accessed – and are thus on the stack - are likely to soon be needed again, so stack operations will be further simplified. Although code written for a stack machine (or a RISC for that matter) often contains a larger number of instructions than code written for a CISC or VLIW computer, the memory size of the code is often smaller, and clock cycles are shorter. Some instructions can even be performed on the same clock cycle - e.g. because the data stack, return stack and memory can be accessed in parallel - and the overall performance is thus very competitive. Some people argue that register machines are more efficient than stack machines because they can use pipelining while stack machines cannot. But as it turns out, stack machines do not need pipelining to achieve the same speed as a RISC. [Koo89] 2.2 UVM Stacks The stack concept in itself supports updatability rather poorly [Öst03]. The very essence of a stack is memorization of previous actions and previous states. And that seriously degrades updatability. The best updatability is achieved when all actions are standalone, plug-in units, as in state machines. In this respect, the ideal system would only allow one active subroutine at a time, and hence only support subroutine calls that "replace" the active subroutine. However, such a system would seem very odd to developers accustomed to writing imperative C-like code, and would not be very well supported by hardware either. As a conclusion,stacks need to be dealt with in realistic systems. 3

TRACE STACK (TS)

chain a chain b

temporary data

chain_ptr DS.ptr TS.ptr brk_cb_ptr nxt_cb_ptr chain_ptr DS.ptr TS.ptr brk_cb_ptr nxt_cb_ptr

count selections & returns count selections & returns

args

chain b

chain b

execution trace

chain block data

chain a

CONTROL STACK (CS)

args

chain a

DATA STACK (DS)

call trace

more temporary data less temporary data

chain data

Illustration 2: The three stacks in a UVM. This illustration shows a scenario where chain a(...) has first been called, and that chain then called chain b(...). While executing chain blocks in the same chain, the temporary data on DS is pushed and popped freely, new selections and returns are pushed to TS, and the next chain block pointer on top of CS is replaced. When moving between chains, the rest of the stack data is also used.

As it turns out, stacks can be used in an updatable-friendly way. The sequence model makes stack content more loosely connected and more easily replaceable. The stack memorizes future actions, but only in the context of chain blocks, and these can be easily rearranged and modified. Stack usage in Uva is further planned such, that not even chain blocks share data on the stack. The only updating issue left is then appropriately dealing with data in respect to the current state. This requires quite a lot of work - automated or manual - to map the state safely and correctly. But this workload cannot be avoided in any software system, and has to be dealt with. The UVM is a pure multiple-stack machine and each thread contains three stacks; a data stack (DS), a control stack (CS) and a trace stack (TS). The control stack is basically an extended return stack, and the trace stack is an extra stack, being unique to Uva. The DS is used like a normal data stack to store temporary data. Control flow is read from the CS, which backs up stack pointers, and declares what chain block is to be executed next. The TS is used for passing return values and selecting branches, but will as a side effect contain the full control flow trace of all active chains. This trace can be used to advantage in update patches. Because return values are stored on the TS, no chain block (except reentrant ones) share data on the DS. Loop and recursion counters are always automatically stored on the TS. Each active chain stores a pointer to itself, a backup of the DS and TS pointers, and a pointer to the "break chain block" on the CS. The break chain block is the chain block where execution should continue if the chain returns with failure, and is typically the same as the next chain block in the calling chain. Furthermore, each chain stores its invocation count on the TS, and the initial values of its arguments on the DS. As the virtual machine executes, a pointer to the "next chain block" will be frequently pushed to and popped from the CS. During execution, there will always be exactly one such element for every active chain, and each one will be located directly on top of the chain related data. Normally, the DS will be emptied before exiting a chain block, and will thus only contain temporary data while inside a chain block. Reentrant chain blocks make an exception to this rule. The trace stack will, for every active chain, contain every selection value, and every final invocation count and return value for called chains. A negative invocation count means that the chain returned with failure, and the real invocation count is in that case the absolute value of the invocation count. Chains that do not return anything will store 0 as their return value. 4

Instead of using linear stacks, independent stack frames can be created for each chain call. A stack frame would then contain entries for the next block pointer, break block pointer, memory that can be used as the TS, and so on. Local variables and arguments are not allocated on the stack but instead have fixed, global addresses. This means that local variables always consume memory, and arguments must be copied to them when a chain is called. But on the other hand, they can be accessed more quickly, and local variables can be efficiently shared. The UVM is - unlike normal stack machines - capable of using fixed local variables since recursion is only allowed through recursive returns.

5

3 UVM Concepts 3.1 Components An internal class representation in a UVM is an extension of the class representation found in a JVM. It contains e.g. a version tag, and in addition to a v-table, it contains an entity-table (e-table). This is an indexed array, for which some elements point to instance chains, some contain instance variable indexes, and the rest are empty. All chains declared in provided features are at load time added to the v-table and static chain table. Static variables declared in features are accessed directly from the features, but memory is reserved for instance fields declared by provided features. At load time, chains are also trimmed as explained later, and the trimmed versions are stored alongside the original chains. All chain pointers point to the trimmed versions, but the UVM must be able to untrim or re-trim chains at demand, and also modify the v-table etc. correctly if a provided feature is reloaded or updated. Internally, the UVM deals with entities by using dynamic entity info structures for each known entity. An entity info contains an index map which is indexed when entity fields and chains are accessed. The index map transforms symbolic referencing of fields and chains to index referencing into the e-table of all compatible classes. The entity indexes will never change, but the transformed indexes will be updated as compatible classes are loaded, updated or unloaded. The purpose with the entity info is to provide fast access to instance fields and chains. Only two pointers need to be dereferenced (one indirection), which is not as fast as in traditional OO, where no indirection is needed, but a) the overhead for accessing a chain this way is low, when compared to actually calling the chain b) most variables will be accessed by the class itself and can then be accessed directly c) chain trimming removes one of the indirections, resulting in as fast access as virtual method calls. Entity infos and e-tables require a small amount of overhead memory, but the key point is that the objects require no overhead memory at all. And object memory consumption is what we really ought to keep low. Static class memory does not weight much in comparison. Whenever a new class is loaded, the entity infos for all entities the class uses are created unless they already exist, and all existing entity infos that the class is compatible with are updated. This is done in the following way. Entity infos that a newly loaded class is compatible with must be revalidated. New entity infos are created and immediately validated for entities that haven't been encountered before. The purpose of validating an entity info is to agree upon some non-conflicting indexing in class etables. Conflicts may arise if some class is compatible with several entities, and two or more different elements in those entity infos point to the same e-table index. An entity info is validated by first calculating the union of all reserved (already used) e-table indexes in all classes compatible with the entity. The lowest indexes are then taken, and every compatible class has their e-table updated. Entity infos are used to transform entity accessing to e-table accessing, and this is done when trimming chains. Chains are trimmed upon loading and re-trimmed as needed; after certain updates, when entity infos they are dependent on have changed, and so on. On the interface level, units must be loosely connected to retain good updatability. But on the execution level, this rule can be broken to improve performance, as long as the interface still follows the rule. In other words, chains are trimmed for better performance in the current system state. And when something changes, they are re-trimmed in the changed system state. 6

#A

#C

After loading T1 z

EntityInfo[#A]: x=0, z=1 T1.symtable: 0=x, 2=z, 1=y EntityInfo[#B]: x=0, z=1, y=2

T1 entry1 get_static #Printer System.out aldc \ "Fibonacci demonstration:\n" + "========================\n\n" + "Enter which term (>= 1) in the Fibonacci series that you want to\n" + "calculate. Add an initial plus sign to have every intermediate term\n" + "calculated as well. Write 'Q' or press Ctrl+D or Ctrl+C to exit." call_entity void #Printer.println(string) entry1 -> return catch IOException -> error call_static void UasmFibonacci.initAndRun() error -> error1 ts_top call_entity string string.toString() error1 -> error2 aldc "An input / output error occurred: " ts_top call_entity string string.concat(string) error2 -> return get_static #Printer System.err ts_top call_entity void #Printer.println(string) asm void deinit(#StreamConnection sc, #InputStream is) throws IOException entry -> entry1 ald is acmp_null sel jnz close_is entry1 ald sc acmp_null sel jnz close_sc ret close_is -> entry1 ald is call_entity void #InputStream.close() close_sc -> return ald sc

56

call_entity void #StreamConnection.close() asm void initAndRun() var #StreamConnection sc #InputStream is #OutputStream os entry -> entry1 aldc "comm:stdio" call_static #Connection Connector.open(string) entry1 -> entry2 ts_top checkcast #StreamConnection ast sc ald sc call_entity #InputStream #StreamConnection.openInputStream() entry2 -> entry3 ts_top ast is new InputStreamReader ts_top ald is call_special void InputStreamReader.init(#InputStream) entry3 -> entry4 ts_top3 ast in ald sc call_entity #OutputStream #StreamConnection.openOutputStream() entry4 -> entry5 ts_top ast os new PrintStream ts_top ald os call_special void PrintStream.init(#OutputStream) entry5 -> return catch NullPointerException -> break ts_top3 ast out loop void UasmFibonacci.runOnce() finally -> return catch IOException -> break ald sc ald is call_static void UasmFibonacci.deinit(#StreamConnection, #InputStream) asm void runOnce() var string strLine bool bIntermediate throws IOException pre -> pre1 ald in ald out call_static string UasmFibonacci.prompt(#Reader, #Printer) pre1 ts_top ast strLine ald strLine acmp_null sel jz entry brk ret entry -> entry1 iconst_0 ist bIntermediate ald strLine call_entity string string.trim() entry1 -> entry2 ts_top ast strLine

57

ald strLine call_entity int string.length() entry2 -> entry3 ts_top sel jgtz char_test nxt entry3 -> return catch NumberFormatException -> error \ IllegalArgumentException -> error ald strLine ild bIntermediate call_static void UasmFibonacci.parseAndCalculate(string, bool) char_test -> char_test1 ald strLine iconst_0 call_entity int string.get(int) char_test1 -> entry3 ts_top sel ipush '+' je char_plus ipush 'q' je char_q ipush 'Q' je char_q nxt char_plus -> char_plus1 iconst_1 ist bIntermediate ald strLine iconst_1 call_entity string string.substring(int) char_plus1 -> entry3 ts_top ast strLine nxt char_q -> char_q1 new NullPointerException ts_top call_special void NullPointerException.init() char_q1 ts_top3 athrow error -> return ald out aldc "Invalid input, use: [+]positive_integer" call_entity void #Printer.println(string) asm void parseAndCalculate(string strCount, bool bIntermediate) var int count string strTmp entry -> entry1 ald strCount ipush 10 call_static int Integer.parseInt(string, int) entry1 -> entry2 ts_top ist count ild bIntermediate sel jnz intermediate nxt intermediate -> entry2 ild count iconst_1 isub loopmax void UasmFibonacci.printIntermediate() entry2 -> entry3 ild count ipush 10 call_static string Integer.toString(int, int) entry3 -> entry4 aldc "Term "

58

ts_top call_entity string string.concat(string) entry4 -> entry5 ts_top aldc " equals " call_entity string string.concat(string) entry5 -> entry6 ts_top ast strTmp iconst_1 iconst_1 ild count call_static int UasmFibonacci.term(int, int, int) entry6 -> entry7 ts_top ipush 10 call_static string Integer.toString(int, int) entry7 -> entry8 ald strTmp ts_top call_entity string string.concat(string) entry8 -> return ald out ts_top call_entity void #Printer.println(string) asm void printIntermediate() var string strTmp entry -> entry1 ts_count ipush 10 call_static string Integer.toString(int, int) entry1 -> entry2 ts_top aldc ": " call_entity string string.concat(string) entry2 -> entry3 ts_top ast strTmp iconst_1 iconst_1 ts_count call_static int UasmFibonacci.term(int, int, int) entry3 -> entry4 ts_top ipush 10 call_static string Integer.toString(int, int) entry4 -> entry5 ald strTmp ts_top call_entity string string.concat(string) entry5 -> return ald out ts_top call_entity void #Printer.println(string) /******************************************************************************/ /* Prompt for input /******************************************************************************/ protected static asm string prompt(#Reader in, #Printer out) var int iChar string strLine throws IOException entry -> entry1 aldc "" ast strLine ald out aldc ">"

59

call_entity void #Printer.print(string) entry1 -> entry2 iconst_0 ipush_w 999 loopminmax void UasmFibonacci.concatenate() entry2 ts_top2 ineg sel iconst_1 jle ret_null ald strLine aret ret_null aconst_null aret asm void concatenate() pre -> pre1 ald in call_entity int #Reader.read() pre1 -> entry ts_top ist iChar ild iChar sel jltz break ipush '\n' je break nxt entry -> entry1 new Character ts_top ild iChar call_special void Character.init(int) entry1 -> entry2 ts_top3 call_entity string #Character.toString() entry2 -> entry3 ald strLine ts_top call_entity string string.concat(string) entry3 ts_top ast strLine ret /******************************************************************************/ /* Calculate a fibonacci term /******************************************************************************/ protected static asm int term(int a, int b, int count) entry -> entry1 ild count sel iconst_1 jlt error iconst_1 je ret_a ild b ild a ild b iadd ild count iconst_1 isub reentr reentrant entry1 -> entry2 call_static int UasmFibonacci.term(int, int, int) reentrant entry2 ts_top iret error -> error1 ild count ipush 10

60

call_static string Integer.toString(int, int) error1 -> error2 aldc "Variable count (=" ts_top call_entity string string.concat(string) error2 ->error3 ts_top aldc ") must be >= 1!" call_entity string string.concat(string) error3 -> error4 new IllegalArgumentException ts_top ts_top2 call_special void IllegalArgumentException.init(string) error4 ts_top3 athrow ret_a ild a iret end // UasmFibonacci

A.2 Polygon Drawing Example version 1.0.1 package draw /##############################################################################/ /## class draw.UasmDraw /## version 1.0.1 /##############################################################################/ class UasmDraw /******************************************************************************/ /* Main chain /******************************************************************************/ public static asm void main(#[string args) entry -> entry1 new UasmPolygon ts_top call_special void UasmPolygon.init() entry1 -> return ts_top3 call_entity void #Polygon.draw() end // Draw /##############################################################################/ /## class draw.UasmPoint /## version 1.0.1 /##############################################################################/ class UasmPoint var int32 x int32 y /******************************************************************************/ /* Constructor /******************************************************************************/ public asm init(int x, int y) entry ald this ild x put_instance int UasmPoint.x ald this ild y put_instance int UasmPoint.y fibonacci /******************************************************************************/ /* Print the coordintes /******************************************************************************/ public asm void show() entry -> entry1 ald this

61

get_instance int UasmPoint.y get_static #Printer System.out aldc "(" call_entity void #Printer.print(string) entry1 -> entry2 ald this get_instance int UasmPoint.x ipush 10 call_static string Integer.toString(int, int) entry2 -> entry3 get_static #Printer System.out ts_top call_entity void #Printer.print(string) entry3 -> entry4 get_static #Printer System.out aldc ", " call_entity void #Printer.print(string) entry4 -> entry5 ald this get_instance int UasmPoint.y ipush 10 call_static string Integer.toString(int, int) entry5 -> entry6 get_static #Printer System.out ts_top call_entity void #Printer.print(string) entry6 -> return get_static #Printer System.out aldc ")" call_entity void #Printer.println(string) end // UasmPoint /##############################################################################/ /## class draw.UasmPolygon /## version 1.0.1 /##############################################################################/ class UasmPolygon var #[#Point points /******************************************************************************/ /* Constructor /******************************************************************************/ public asm init() entry -> entry1 iconst_5 newaarray #Point ald this ts_top put_instance #[#Point UasmPolygon.points new UasmPoint ts_top iconst_0 ipush -5 call_special void UasmPoint.init(int, int) entry1 -> entry2 ald this get_instance #[#Point UasmPolygon.points iconst_0 ts_top3 call_entity void #[#Point.put(int, #Point) entry2 -> entry3 new UasmPoint ts_top iconst_5 iconst_0 call_special void UasmPoint.init(int, int) entry3 -> entry4 ald this get_instance #[#Point UasmPolygon.points iconst_1 ts_top3 call_entity void #[#Point.put(int, #Point) entry4 -> entry5 new UasmPoint

62

ts_top iconst_0 iconst_5 call_special void UasmPoint.init(int, int) entry5 -> entry6 ald this get_instance #[#Point UasmPolygon.points iconst_2 ts_top3 call_entity void #[#Point.put(int, #Point) entry6 -> entry7 new UasmPoint ts_top ipush -5 iconst_0 call_special void UasmPoint.init(int, int) entry7 -> entry8 ald this get_instance #[#Point UasmPolygon.points iconst_3 ts_top3 call_entity void #[#Point.put(int, #Point) entry8 -> entry9 new UasmPoint ts_top iconst_0 ipush -5 call_special void UasmPoint.init(int, int) entry9 -> return ald this get_instance #[#Point UasmPolygon.points iconst_4 ts_top3 call_entity void #[#Point.put(int, #Point) /******************************************************************************/ /* Show the coordinates of the polygon /******************************************************************************/ public asm void show() entry -> entry1 ald this get_instance #[#Point UasmPolygon.points call_entity int #[#Point.length() entry1 -> return ts_top loopmax void UasmPolygon.showOne() asm void showOne() entry -> entry1 ald this get_instance #[#Point UasmPolygon.points ts_count iconst_1 isub call_entity #Point #[#Point.get(int) entry1 -> return ts_top call_entity void #Point.show() /******************************************************************************/ /* Draw the polygon /******************************************************************************/ public asm void draw() var int yMin int yMax int x1 int y entry -> entry1 ald this get_instance #[#Point UasmPolygon.points iconst_0

63

call_entity #Point #[#Point.get(int) entry1 -> entry2 ts_top get_entity int #Point.y dup ist yMin ist yMax ald this get_instance #[#Point UasmPolygon.points call_entity int #[#Point.length() entry2 -> entry3 ts_top iconst_2 isub loopmax void UasmPolygon.findMinMax() entry3 -> return get_static int Integer.MIN_VALUE ist x1 ild yMin ist y loop void UasmPolygon.oneRow() asm void findMinMax() entry -> testMin ald this get_instance #[#Point UasmPolygon.points ts_count call_entity #Point #[#Point.get(int) testMin ts_top get_entity int #Point.y sel ild yMin jge testMax ts_top ist yMin ret testMax ild yMax jle return ts_top ist yMax ret asm void oneRow() pre -> entry ild y sel ild yMax jgt break nxt entry -> entry1 ald this get_instance #[#Point UasmPolygon.points call_entity int #[#Point.length() entry1 -> entry2 ts_top iconst_1 isub loopmax void UasmPolygon.findIntersection() entry2 -> entry3 ild x1 sel get_static int Integer.MIN_VALUE je post ild x1 dup call_static void UasmPolygon.drawLine(int, int) entry3 -> post get_static int Integer.MIN_VALUE ist x1 nxt post iinc y 1

64

ret asm void findIntersection() var int x #Point p1 #Point p2 entry -> entry1 ald this get_instance #[#Point UasmPolygon.points ts_count iconst_1 isub call_entity #Point #[#Point.get(int) entry1 -> test1 ts_top ast p2 ald this get_instance #[#Point UasmPolygon.points ts_count call_entity #Point #[#Point.get(int) test1 -> found ts_top ast p1 ild y sel ald p1 get_entity int #Point.y jlt test2 ald p2 get_entity int #Point.y jgt test2 nxt test2 -> found ald p1 get_entity int #Point.y jgt return ald p2 get_entity int #Point.y jlt return nxt found ald p1 get_entity int #Point.x ild y ald p1 get_entity int #Point.y isub ald p2 get_entity int #Point.x ald p1 get_entity int #Point.x isub ald p2 get_entity int #Point.y ald p1 get_entity int #Point.y isub idiv imul iadd ist x ild x1 sel get_static int Integer.MIN_VALUE jne twoFound ild x ist x1 ret twoFound -> twoFound1 ild x je return ild x1 ild x

65

call_static void UasmPolygon.drawLine(int, int) twoFound1 get_static int Integer.MIN_VALUE ist x1 brk ret /******************************************************************************/ /* Draw one single line /******************************************************************************/ public static asm void drawLine(int x1, int x2) entry -> spaces ild x1 sel ild x2 jle spaces ild x1 ild x2 ist x1 ist x2 nxt spaces -> asterixes ild x1 ipush 10 iadd aldc " " loopmax void UasmPolygon.printString(string) asterixes -> endline ild x2 ild x1 isub iconst_1 iadd aldc "*" loopmax void UasmPolygon.printString(string) endline -> return get_static #Printer System.out call_entity void #Printer.println() asm void printString(string s) entry -> return get_static #Printer System.out ald s call_entity void #Printer.print(string) end // UasmPolygon

A.3 The Dining Philosophers Example version 1.0.0 package philosophers import java.util /##############################################################################/ /## public class philosophers.UasmDinnerTable /## version 1.0.0 /##############################################################################/ public class UasmDinnerTable var protected static #[bool forks protected static #[#Object keys private static #Random rand /******************************************************************************/ /* Main chain /******************************************************************************/ public static asm void main(#[string args) var int n entry -> entry0 new Random

66

ts_top call_special void Random.init() entry0 -> entry1 ts_top3 put_static #Random this.rand get_static #Printer System.out aldc "Dining Philosophers demonstration:\n" + "==================================\n" call_entity void #Printer.println(string) entry1 -> entry2 ald args call_entity int #[string.length() entry2 -> entry3 iconst_1 jne wrong_arg_count ald args iconst_0 call_entity string #[string.get(int) entry3 -> entry4 catch Exception -> nonint_arg ts_top ipush 10 call_static int Integer.parseInt(string, int) entry4 -> entry5 ts_top ist n get_static #Printer System.out aldc "Press + C to exit." call_entity void #Printer.println(string) entry5 -> entry6 ild n iconst_1 iadd dup newarray bool newaarray #Object ts_top2 put_static #[bool this.forks ts_top put_static #[#Object this.keys get_static #[#Object this.keys iconst_0 get_static #[bool this.forks call_entity void #[#Object.put(int, #Object) entry6 -> entry7 ild n loopmax void this.createPhilosopher() entry7 -> return ild n loopmax void this.startPhilosopher() nonint_arg -> return get_static #Printer System.out aldc "Needs an integer parameter!" call_entity void #Printer.println(string) wrong_arg_count -> return get_static #Printer System.out aldc "Needs exactly one integer parameter!" call_entity void #Printer.println(string) asm void createPhilosopher() entry -> entry1 new UasmPhilosopher ts_top ts_count call_special void UasmPhilosopher.init(int) entry1 -> return get_static #Object this.keys ts_count ts_top3 call_entity void #[Object.put(int, #Object) asm void startPhilosopher() entry -> entry1 get_static #Object this.keys ts_count

67

call_entity #Object #[Object.get(int) entry1 -> return ts_top checkcast #Philosopher call_entity void #Philosopher.start() /******************************************************************************/ /* Get a random value in a given range /******************************************************************************/ public static asm int random(int iMin, int iMax) entry -> entry1 get_static #Random this.rand call_entity int #Random.nextInt() entry1 -> entry2 ts_top call_static int Math.abs(int) entry2 ts_top ild iMax iconst_1 iadd ild iMin isub irem ild iMin iadd iret /******************************************************************************/ /* Ask how many forks we've got /******************************************************************************/ public static asm int getForkCount() entry -> entry1 get_static #[bool this.forks call_entity int #[bool.length() entry1 ts_top iret /******************************************************************************/ /* Grab two forks /******************************************************************************/ public static asm bool grabForks(int iFork1, int iFork2) var bool success entry -> lock1 iconst_0 ist success get_static #[#Object this.keys ild iFork1 call_entity #Object #[#Object.get(int) lock1 -> lock2 ts_top monitorenter get_static #[#Object this.keys ild iFork2 call_entity #Object #[#Object.get(int) lock2 -> test1 ts_top monitorenter get_static #[bool this.forks ild iFork1 call_entity bool #[bool.get(int) test1 -> test2 get_static #[bool this.forks ild iFork2 call_entity bool #[bool.get(int) test2 -> unlock1 ts_top3 ts_top lor lneg

68

sel jnz grab_forks1 nxt unlock1 -> unlock2 get_static #[#Object this.keys ild iFork2 call_entity #Object #[#Object.get(int) unlock2 -> unlock3 ts_top monitorexit get_static #[#Object this.keys ild iFork1 call_entity #Object #[#Object.get(int) unlock3 ts_top monitorexit ild success iret grab_forks1 -> grab_forks2 iconst_1 ist success get_static #[bool this.forks ild iFork1 iconst_1 call_entity void #[bool.put(int, bool) grab_forks2 -> unlock1 get_static #[bool this.forks ild iFork2 iconst_1 call_entity void #[bool.put(int, bool) /******************************************************************************/ /* Release one single fork /******************************************************************************/ public static asm void releaseFork(int iFork) entry -> lock get_static #[#Object this.keys ild iFork call_entity #Object #[#Object.get(int) lock -> unlock1 ts_top monitorenter get_static #[bool this.forks ild iFork iconst_0 call_entity void #[bool.put(int, bool) unlock1 -> unlock2 get_static #[#Object this.keys ild iFork call_entity #Object #[#Object.get(int) unlock2 ts_top monitorexit ret end // UasmDinnerTable /##############################################################################/ /## public class philosophers.UasmPhilosopher /## extends Thread /## version 1.0.0 /##############################################################################/ class UasmPhilosopher extends Thread var public static const int32 RETRY_TIME public static const int32 MIN_TIME public static const int32 MAX_TIME protected int32 iId protected int32 iThinkTime protected int32 iEatTime /******************************************************************************/ /* Static initializer

69

/******************************************************************************/ public static asm clinit() entry ipush 20 put_static int this.RETRY_TIME ipush 100 put_static int this.MIN_TIME ipush_w 2000 put_static int this.MAX_TIME ret /******************************************************************************/ /* Constructor /******************************************************************************/ public asm init(int iId) entry -> entry1 ald this ild iId put_instance int this.iId get_static int this.MIN_TIME get_static int this.MAX_TIME call_static int UasmDinnerTable.random(int, int) entry1 -> entry2 ald this ts_top put_instance int this.iThinkTime get_static int this.MIN_TIME get_static int this.MAX_TIME call_static int UasmDinnerTable.random(int, int) entry2 ald this ts_top put_instance int this.iEatTime ret /******************************************************************************/ /* Thread loop /******************************************************************************/ public asm void run() entry -> entry1 ald this aldc "joined the happy diners." call_virtual void this.notify(string) entry1 -> return loop void this.runLoop() asm void runLoop() entry -> entry1 ald this dup get_instance int this.iThinkTime call_virtual void this.sleepSafely(int) entry1 -> entry2 ald this aldc "longs for spaghetti and goes for his forks." call_virtual void this.notify(string) entry2 -> entry3 ald this call_virtual void this.grabForks() entry3 -> entry4 ald this aldc "got both forks and starts eating like mad!" call_virtual void this.notify(string) entry4 -> entry5 ald this dup get_instance int this.iEatTime call_virtual void this.sleepSafely(int) entry5 -> entry6 ald this call_virtual void this.releaseForks() entry6 -> return ald this aldc "has eaten and makes an attempt at making " +

70

"the world a better place." call_virtual void this.notify(string) /******************************************************************************/ /* Sleep and catch interruption exceptions /******************************************************************************/ public asm word{64} void sleepSafely(int iMilliseconds) entry -> return ild iMilliseconds call_static void Thread.sleep(int) catch InterruptedException -> return ald this aldc "was interrupted." call_virtual void this.notify(string) /******************************************************************************/ /* Show a note about what this philosopher is doing /******************************************************************************/ public asm void notify(string strMsg) entry -> entry1 ald this get_instance int this.iId ipush 10 call_static string Integer.toString(int, int) entry1 -> entry2 aldc "Philosopher " ts_top call_entity string string.concat(string) entry2 -> entry3 ts_top aldc " " call_entity string string.concat(string) entry3 -> entry4 ts_top ald strMsg call_entity string string.concat(string) entry4 -> return get_static #Printer System.out ts_top call_entity void #Printer.println(string) /******************************************************************************/ /* Grab both the left hand and the right hand fork /******************************************************************************/ public asm void grabForks() var int iFork1 int iFork2 entry -> entry1 call_static int UasmDinnerTable.getForkCount() entry1 -> return ald this get_instance int this.iId dup ist iFork1 iconst_1 iadd ts_top irem ist iFork2 loop void this.tryToGrabForks() asm void tryToGrabForks() pre -> pre1 ild iFork1 ild iFork2 call_static bool UasmDinnerTable.grabForks(int, int) pre1 jz entry brk ret entry -> entry1

71

get_static #Printer System.out aldc "." call_entity void #Printer.print(string) entry1 -> return ald this get_static int this.RETRY_TIME call_virtual void this.sleepSafely(int) /******************************************************************************/ /* Release both forks /******************************************************************************/ public asm void releaseForks() var int iFork1 int iFork2 entry -> entry1 call_static int UasmDinnerTable.getForkCount() entry1 -> entry2 ald this get_instance int this.iId dup ist iFork1 iconst_1 iadd ts_top irem ist iFork2 ild iFork1 call_static void UasmDinnerTable.releaseFork(int) entry2 -> return ild iFork2 call_static void UasmDinnerTable.releaseFork(int) end // UasmPhilosopher

72

References [CLDC]

Antero Taivalsaari, Sun Microsystems, Inc., J2ME Connected, Limited Device Configuration,

[KK94]

Mika Keskikiikonen and Petri Kiuttu, PC-pelintekijän opas, 1994

[Koo89]

Philip J Koopman, Jr., Stack Computers - The New Wave, 1989

[LY96]

Tim Lindholm and Frank Yellin, The Java Virtual Machine Specification, 1996

[M68HC11]

Motorola Inc., M68HC11 Reference Manual,

[Öst03]

Dan Österberg, Rethinking Software Updating; Concepts for Improved Updatability, Åbo Akademi University, 2003

73

Turku Centre for Computer Science Lemminkäisenkatu 14 FIN-20520 Turku Finland http://www.tucs.fi/

University of Turku • Department of Information Technology • Department of Mathematics

Åbo Akademi University • Department of Computer Science • Institute for Advanced Management Systems Research

Turku School of Economics and Business Administration • Institute of Information Systems Science