JVM and Jasmin Tutorial

This tutorial covers only a subset of JVM specification, which is enough for a person who will use JVM as a target language for C+-.

    1. Overview

    JVM (Java Virtual Machine) is a piece of software, a "virtual" processor, by which Java programs are run. It is an interface between your Java program and the hardware your system uses. It is not a Java compiler, it is the machine which runs the compiled Java code. It is the mechanism which makes Java portable.

    By virtue of being a virtual machine, JVM, can as well be an interface between another programming language and the hardware, which will also make that language portable. So, 'C+- is a portable programming language' is perfectly a true statement, for anyone who is saying 'Java is a portable programming language'.

    Like all other virtual machines, JVM has an instruction set. There are nearly 255 instructions in this set, however you will not be dealing with all of them.

    JVM takes "class" files as input. Class files are binary files, so it is not an easy job to write a class file from scratch. So what you need is a tool to convert an ASCII JVM code to a binary class file. Here's where Jasmin comes into picture. You can think of Jasmin as a Java Assembler. So, in the third phase of your project, you will be producing Jasmin files instead of binary "class" files, which will ease your job a lot.
     
     

    2. A Simple C+- Example Translated into Jasmin :

    The following is a quick introduction to JVM and Jasmin.

    A simple program in C+- :

    int main()
    {
         int x;
         x=input();
         x=x+3;
         output(x);
         return 0;
     }

    The same program in Jasmin :

    .class public simple
    .super java/lang/Object

    .method public <init>()V
             aload_0
             invokespecial java/lang/Object/<init>()V
             return
    .end method

    .method public static main([Ljava/lang/String;)V
        .limit stack 5
        .limit locals 100

        ldc 0
        istore 1      ; initialize x to zero and store it in local variable 1
     

                              ; the input function starts at this point
          ldc 0
        istore 50     ; storage for a dummy integer for reading it by input()
     Label1:
        getstatic java/lang/System/in Ljava/io/InputStream;
        invokevirtual java/io/InputStream/read()I
        istore 51
        iload 51
        ldc 10
        isub
        ifeq Label2
        iload 51
        ldc 32
        isub
        ifeq Label2
        iload 51
        ldc 48
        isub
        ldc 10
        iload 50
        imul
        iadd
        istore 50
        goto Label1

      Label2:          ; now our dummy integer contains the integer read from the keyboard
        iload 50       ; input function ends here
        istore 1       ; store this value in x
        iload 1
        ldc 3
        iadd
        istore 1       ; x=x+3
        iload 1
        getstatic java/lang/System/out Ljava/io/PrintStream;
        swap
        invokevirtual java/io/PrintStream/println(I)V   ;output(x)
        return        ; return from main

    .end method
     

    The Jasmin code for the program  looks quite complicated compared to the C+- version, but you do not need to know all the things around. Most of the code you see above will be present in all of the files you generate. So you can consider the above Jasmin code as a template.

    A Jasmin file consists of directives, labels and instructions. The lines beginning with a "." (dot) are directives, labels are names followed by a ":", and a newline. The rest are considered as instructions.

    .class directive tells Jasmin the name of the class being defined.

    .super directive tells Jasmin the class which our class is extending. It will be java/lang/Object for all C+- programs.

    The method (function) definitions begin with a .method directive and end with .end method directive.

    The <init> method is an instance initialization method and again will be present for all C+- programs. It is used to initialize a new instance of the class. This is a process, which also includes initialization of the inherited class, which, in this case, is java/lang/Object.

    The main method is where the translated code of your C+- program resides. Notice that the main function has a parameter which is an array of strings and it returns void. The main function for JVM has to have these specifications to qualify as an entry point to the program.

    .limit stack 5 directive in main method sets the size of the operand stack for method "main" to 5. This means, we can push up to five items on that method's operand stack during the execution of the method. Each method has its own operand stack in JVM.

    .limit locals 100 directive sets the number of local variables in method "main" to 100. It is set to 1 by default. Use this directive in a method which has more than one local variable.

    The rest in the above code are the instructions, which are explained in detail in 'The Instruction Set' section.
     

    3. Type Descriptors

    When you're defining a method (function) with a .method directive, you specify the type of parameters and the return type by using type descriptors. You will not be using these directives in your program since your only method declaration will be the main method. Other than main method you can declare input and output methods by default in every Jasmin file you produce. The Jasmin code for these built-in functions are given at the end of this tutorial. Anyway, to help you understand the function calls used in a Jasmin program for the built-in C+- functions, input and output, here are the basic type descriptors that may used in a Jasmin program:

    F    single-precision IEEE 754 float

    I     integer

    L<classname>;               an instance of the class

    [     one array dimension

    V   void for return type

    example :

    int f(int a, string b,float c)

    will have a descriptor,

    .method public static f(ILjava/lang/String;F)I
     

    Now, take a look at the simple program's main method :

    .method public static main([Ljava/lang/String;)V
     

    It is a method which takes an array of strings as a parameter and returns no result. Notice that string is not a primitive type in JVM, so we have to use the Ljava/lang/String descriptor for strings, which represents the String object of Java.
     

    4. The Instruction Set

          The instructions of JVM are presented in the following format :
 

       mnemonic { parameters }

         brief description

         (stack before => stack after)
 
 

         Here are the instructions you may need to use :
 
 

        Load and Store Instructions :

        iload <local_variable_number>

         pushes the value of the local variable which is an integer.

         (.... => .....value)
 
 

        istore <local_variable_number>

         pops the value which is an integer and stores it in local variable.

         (.....value => ......)
 

        fload <local_variable_number>

        pushes the value of the local variable which is a float.

        (..... => ......value)
 

       fstore <local_variable_number>

        pops the value which is a float and stores it in local variable.

        (.....value => ......)
 

       ldc <constant>

        push constant on the stack.

        (....... => .......constant)
 

      Stack Manipulation Instructions :

       dup

        the word on top of the stack is duplicated.

        (......word => ......word,word)
 

        pop

         pop top word off the operand stack.

         (.....word => .......)
 

        swap

         swap two operand stack words.

        (.......word1, word2 => ........word2, word1)
 
 

      Arithmetic Instructions :
 

         iadd

         add int.

         (.......value1, value2 => ...........value1+value2)
 

          idiv

         divide int

         (.......value1, value2 => ..........value1/value2)
 

         imul

         multiply int

         (.........value1, value2 => ..........value1*value2)
 

         isub

         subtract int

         (.......value1, value2 => ..........value1-value2)
 

         fadd

         add float

         (........value1, value2 => ........value1+value2)
 
 

         fdiv

        divide float

        (.........value1, value2 => .......value1/value2)
 

        fmul

        multiply float

        (........value1, value2 => .......value1*value2)
 

         fsub

         subtract float

        (........value1, value2 => ........value1-value2)
 
 

      Branch Instructions :
 

        goto <label>

        jump to label

       (no change in stack)
 
 

      ifeq <label>

       jump to label if the value on top of stack is 0

       (.......value => ........)
 

      ifge <label>

       jump to label if the value on top of the stack is >=0

       (.........value => .........)
 

      ifgt <label>

       jump to label if the value on top of the stack is >0

      (........value => ..........)
 

      ifle <label>

       jump to label if the value on top of the stack is <=0

      (........value => .........)
 

      iflt <label>

       jump to label if the value on top of the stack is <0

      (........value => ........)
 

     ifne <label>

      jump to label if the value on top of the stack is not equal to 0

      (........value => ..........)
 

       Logical Instructions :

      iand

value1 and value2, which must be of type int, are popped from the operand stack and an int result is   calculated by taking the bitwise AND (conjunction) of value1 and value2, then the result is pushed onto the operand stack.
       (..........value1,value2 => .........result)
 

      ior

value1 and value2, which must be of type int, are popped from the operand stack and an int result is calculated by taking the bitwise OR (disjunction) of value1 and value2, then the result is pushed onto the operand stack.
       (..........value1,value2 => ........result)
 
 

      Conversion Instructions :
 

         i2f

         convert int to float.

         (.........int_value => ........float_value)
 
 

        f2i

        convert float to int.

        (........float_value => .......int_value)
 

      Subroutine Instructions:

         jsr  <label>

         Pushes the return address on the stack and jumps to subroutine indicated by the label.

          ( ...... => ......returnAddress)
 
 

         ret <local_variable_number>

          Returns from subroutine to the return address which is stored in a local variable.

          (no change)
 

To implement a function in C+- as a subroutine, what you have to do is to push the arguments in the caller statement  and jump to the beginning of the function by using 'jsr'. In the beginning of the function you have to pop the return address and store it in a local variable, and pop the argument, store them in local variables. For return you have to         push the return value and use 'ret' instruction to return to the execution point just after the 'jsr' instruction. With 'ret', you have to us the local variable number that you used to store the return address.
 
 

5. Standard Library Functions of C+-

    The Jasmin method declarations for the built-in C+- functions input and output are given in this section.

    int input()

    .method public static input()I
          .limit locals 10
          .limit stack 10

          ldc 0
          istore 1  ; this will hold our final integer
    Label1:
          getstatic java/lang/System/in Ljava/io/InputStream;
          invokevirtual java/io/InputStream/read()I
          istore 2
          iload 2
          ldc 10   ; the newline delimiter
          isub
          ifeq Label2
          iload 2
          ldc 32   ; the space delimiter
          isub
          ifeq Label2

          iload 2
          ldc 48   ; we have our digit in ASCII, have to subtract it from 48
          isub
          ldc 10
          iload 1
          imul
          iadd
          istore 1
          goto Label1
    Label2:
          ;when we come here we have our integer computed in Local Variable 1
          iload 1
          ireturn
    .end method

    int output(int)

    .method public static output(I)I
          .limit locals 5
          .limit stack 5

          iload 0
          getstatic java/lang/System/out Ljava/io/PrintStream;
          swap
          invokevirtual java/io/PrintStream/println(I)V
          ldc 0
          ireturn
    .end method

 

          6. An example of C+- => Jasmin

In this section a C+- program which has a recursive function is translated into Jasmin.
This example will give you an idea on handling function calls, especially recursive ones. Here is our C+- code for the example:

int  main()
{
    int x;
    int y;

    int gcd (int u, int v)
    {
        int tmp;

        if (u < v)
           {
            tmp=u;
            u=v;
            v=tmp;
           }
        if (v == 0) return u ;
        else return gcd(v,u-v);
    }

    /* statements of main */
    x = input();
    y = input();
    output(gcd(x,y));

return 0;
}

Here is the Jasmin code for the above C+- program:

.class public gcd
.super java/lang/Object

.method public <init>()V
      aload_0
      invokespecial java/lang/Object/<init>()V
      return
.end method
 

.method public static input()I
     .limit locals 10
     .limit stack 10

     ldc 0
     istore 1  ; this will hold our final integer
 Label1:
     getstatic java/lang/System/in Ljava/io/InputStream;
     invokevirtual java/io/InputStream/read()I
     istore 2
     iload 2
     ldc 10   ; the newline delimiter
     isub
     ifeq Label2
     iload 2
     ldc 32   ; the space delimiter
     isub
     ifeq Label2

     iload 2
     ldc 48   ; we have our digit in ASCII, have to subtract it from 48
     isub
     ldc 10
     iload 1
     imul
     iadd
     istore 1
     goto Label1
  Label2:
     ;when we come here we have our integer computed in Local Variable 1
     iload 1
     ireturn
.end method
 

.method public static output(I)I
     .limit locals 5
     .limit stack 5

     iload 0  ; the argument to function
     getstatic java/lang/System/out Ljava/io/PrintStream;
     swap
     invokevirtual java/io/PrintStream/println(I)V
     ldc 0
     ireturn
.end method
 

.method public static main([Ljava/lang/String;)V
  .limit stack  150
  .limit locals 150

  ldc 0
  istore 0  ; initialize x
  ldc 0
  istore 1  ; initialize y
  goto Lmain

Lgcd:
  istore 10   ; store return address in local variable 10
  istore 12   ; store y in local variable 12 (v in this scope)
  istore 11   ; store x in local variable 11 (u in this scope)

  ldc 0
  istore 13   ; initialize tmp

  iload 11
  iload 12
  isub      ; u-v
  iflt label1
  goto label2
label1:
  iload 11
  istore 13 ; tmp=u
  iload 12
  istore 11 ; u=v
  iload 13
  istore 12 ; v=tmp
label2:
  ldc 0
  iload 12
  isub
  ifeq label3
  goto label4
label3:
  iload 11
  ret 10       ; return from gcd call
label4:
  iload 10
  iload 11
  iload 12
  iload 13  ; push all locals on stack before the recursive call

  iload 12  ; push argument v
  iload 11
  iload 12
  isub      ; push argument u-v

  jsr Lgcd  ; the recursive call

  swap      ; this is for restoring the locals we pushed before jsr
  istore 13 ; the stack is (....locals,returned_result)
  swap      ; this "swap" solution works since our recursive
  istore 12 ; function has one return value
  swap      ; as all other C+- functions.
  istore 11
  swap
  istore 10
            ; now we have restored our locals
            ; the stack is (....returned_result)

  ret 10    ; we return the returned_result of the recursive call
 

Lmain:
  invokestatic io/input()I
  istore 0                 ; x=input()
  invokestatic io/input()I
  istore 1                 ; y=input()
  iload 0
   iload 1  ; note : y is on top of x
  jsr Lgcd
  invokestatic io/output(I)I ; we have the result of gcd on the stack

  return
.end method
 

7. How to Run Jasmin

>jasmin myprog.j
Generated myprog.class
>java myprog