In Chapter 1: Supplements
: The JVM we gave an example of a Java class file in bytecode
format using output from the javap
tool. Here we will look at the instructions of the JVM that relate
to the primitive data type and arithmetic topics discussed in the
Java and Tech
sections of this chapter. After the introduction to classes and
arrays in later chapters, we will discuss the instructions that
involve these structures in Chapter
5: Supplements : JVM Instructions Part 2 .
Opcodes & Data Types
The one byte instruction word in the Java Virtual
Machine provides for up to 256 instructions but the bytecode currently
only uses 200 of these in the class files plus 25 so-called "quick"
commands that the JVM can use internally. New instructions may be
added later.
Below we group the instructions according to their
general operation and list them under their opcode mnemonic
name. For example, the instruction code 0x04(4)
is referred to by the opcode iconst_1,
which pushes the integer value 1 onto the operand stack.
Recall that the JVM uses stacks (last-in-first-out memory
buffers) rather than registers for the bytecode operations. It
also loads/stores data values from/to a cache, usually referred
to as the heap, that holds field values.
The i
in iconst_1
indicates an operation involving an integer. As discussed in Chapter
2: Java Primitive Types, Java is a strongly typed language and
so all data values must maintain a clear type identification. The
opcodes reflect this design and many instructions differ only in
the type of operands involved.
For example, all the following instructions push the
value of 1 onto the stack but differ according to the type of the
value:
- iconst_1
pushes int 1
onto the stack
- lconst_1
pushes long 1
onto the stack
- fconst_1
pushes float
1.0 onto the stack
- dconst_1
pushes double
1.0 onto the stack
Note that the long
and double
values require two of the 32 bit wide words on the stack.
The opcodes, however, are not completely symmetric
with respect to types. For example, there are six of the int
type constant value instructions for pushing values 0 (iconst_0)
through 5 (iconst_5)
onto the stack but only two such instructions for long type (lconst_0,
lconst_1),
three for float (fconst_0,
fconst_1,
fconst_2)
and two for double (dconst_0,
dconst_1).
This asymmetry comes from the designers wishing to
conserve the finite number of instructions available and from the
fact that the integer operations occur more frequently on average.
In the instructions below, the following letters indicate
the particular data types:
- i = int
- l = long
- f = float
- d = double
- b = byte
- c = char
- s = short
- a = reference
(for accessing objects - see Chapter
3.)
Arithmetic Opcodes
Each of the addition instructions - iadd,
ladd, fadd, dadd - pops the top two operands from the stack,
adds them, and then pushes the result on top of the stack. The data
type of the operands and that of the instruction must match.
In the diagram below, val1
represents a 32 bit wide value on top of the stack and val2
represents the second value from the top. For the iadd
instruction, the two operands must each contain an integer data
type. The operation results in val1
holding the sum of the two values.
Stack Before |
operation |
Stack After |
|
xadd
for
x = i
or
x = f |
|
A fadd
operation works in a similar manner except that the operands and
sum will be float
type values
The long
and double
operations expect the top two 32 bit words to hold the first operand
and the third and fourth words to hold the second operand. Java
follows a big-endian representation (most significant byte
is at lowest memory address) so the first bytes (in the table called
the hi segment)
are the most significant.
Stack Before |
operation |
Stack Before |
val1.hi
|
val1.lo |
val2.hi |
val2.lo |
.... |
|
xadd
for
x = l
or
x = d |
val1.hi=sum.hi |
val2.lo=sum.lo |
.... |
.... |
...
|
|
Note that the JVM keeps track of the data types on the stack. So,
for example, you cannot push two int
values onto the stack and then treat them as a single long
or double value.
In the above case, you cannot push four int
values onto the stack and then add them with the ladd
or dadd.
The other arithmetic operations follow a similar pattern:
Arithmetic
Operations |
Addition:
iadd,
ladd,
fadd,
dadd
val1
+ val2 => val1
Subtraction:
isub,
lsub,
fsub,
dsub
val1
- val2 => val1
Multiplication:
imul,
lmul,
fmul,
dmul
val1 * val2 =>
val1
Division:
idiv,
ldiv,
fdiv,
ddiv
val1 / val2 => val1
Remainder:
irem,
lrem,
frem,
drem
Remainder of val1/val2 => val1
Negate:
ineg,
lneg,
fneg,
dneg
-val1 => val1
Local
variable increment:
iinc
Exception to the rule that operands come from the stack
. In this case, the next byte after the instruction provides
an index to a local variable and the byte after that gives
the amount to add to that local variable. No change to
the stack occurs.
When wide
is placed in front of iinc,
it extends the increment's range from 8 bits to 16 bits.
See Java language arithmetic operators in Chapter
2: Java : Operators and also Increment
& Decrement operators.
|
More notes on arithmetic instructions:
- As discussed in Chapter
2: Tech : Floating Point Part 2, invalid FP operations such
as a divide by zero do not produce an error condition. Instead,
the result receives a special value, as in NaN
for the zero divide case. See that section for the values
that result for these exceptional cases.
- On the stack, byte,
char
and short
values reside in the 32 bit wide words, though the JVM keeps track
of what type each word represents. So no particular speedup in
the integer arithmetic instructions comes with using a more narrow
data type. On the heap, however, the JVM can save space by storing
the data according to its particular width.
The following Bit and Comparison instructions, while not arithmetic,
also result in a new numerical value.
Bit
Instructions |
As with the other arithmetic operations,
the bit instructions replace val1
and val2
with the result in val1.
Shift:
ishl
Shift val1
left by val2
bits using the value in the lowest 5 bits of val2.
ishr
Shift val1
right by val2
bits using the value in the lowest 5 bits of val2.
The upper bits are filled with the
sign value.
iushr
Same as ishr
but fills left bits with zero.
lshl, lshr,
lushr
Same as above except
val1 is
a long value and 6 bits of int val2
are used.
Bitwise
OR:
ior
Logical
OR of bits in val1
and val2
for integer types.
lor
Logical OR
of bits in val1
and val2
for long
types.
Bitwise
AND:
iand
Logical AND
of bits in val1
and val2
for integer types
land
Logical AND
of bits in val1
and val2
for long
types
Bitwise
exclusive OR:
ixor
Logical XOR
of bits in val1
and val2
for integer types
lxor
Logical XOR
of bits in val1
and val2
for long
types
The Java language equivalents can be seen at Chapter
2: Java : Operators : Boolean and Bitwise
Operators.
|
Comparisons |
In all cases below, the operation replaces
val1 &
val2 with
result in an integer val1.
dcmpg
Comparison of double
values val1
and val2
results in an int
val1 equal
to
0 if val1 == val2
1 if va1 < val2
-1 if val1 > val2
1 if either val1 or
val2 equal NaN.
dcmpl
Same as dcmpg
except val1 =
-1 if either val1
or val2
equal NaN.
fcmpg, fcmpl
Same as corresponding dcmpg,dcmpl
except with float
type values.
lcmp
Comparison of long
values val1
and val2
results in an int
val1 equal to
0 if val1 == val2
1 if va1 < val2
-1 if val1 > val2
The Java language equivalents can be seen at Chapter
2: Java : Operators : Comparison.
|
More Opcodes
We list here more of the JVM opcodes, concentrating on those that
carry out the Java language operations discussed in the Java track
of this chapter. In Chapter
5: Supplements : Java Instructions Part 2 we concentrate on
the instructions that deal with object handling.
As above, val1 and val2 correspond to the top two values on the
stack. A val1 or val2 corresponds to one 32 bit word for an integer
data type and two 32 bit words for one long data type.
Conversion
Instructions |
These instructions convert val1
from one type to another. In the form x2y,
val1 of type
x becomes
converted to val1
of type y.
- Widening:
i2l, i2f, i2d, l2f, l2d,
f2d
- Narrowing:
i2b, i2c, i2s, l2i, f2i, f2l, d2i, d2l, d2f
See Chapter
2: Java : Casts & Mixing Primitive Types and Chapter
2: More Casting & Mixing for discussions of conversions
among data types in the Java language.
|
Flow
Control Instructions |
In all cases below, the stack values (either
one or two values depending on the instruction) are popped
off and no new stack value created. The <label>
symbols represents an address (actually
an offset relative to the current instruction) in the bytecode
itself, not a stack value.
Conditional
branch:
ifXX <label>
val1
is popped off the stack and compared to zero according
to the XX
test. If val1
satisfies XX,
the process jumps to the instruction at the bytecode address
indicated by <label>.
ifeq:
jump if val1 == 0
iflt: jump if val1 < 0
ifle: jump if val1 <= 0
ifne: jump if val1 != 0
ifgt: jump if val1 > 0
ifge: jump if val1 > 0
ifnull: jump if val1 >= 0
ifnonnull : jump if val1 != null reference
if_icmpXX
<label>
val1 and val2
are popped off the stack and compared to each other according
to the XX
test. If it they satisfy the test, the process jumps to
the instruction at the byteocde address indicated by <label>.
if_icmpeq:
jump if val2 == val1
if_icmpne: jump if val2 != val1
if_icmplt: jump if val2 < val1
if_icmpgt: jump if val2 > val1
if_icmple: jump if val2 <= val1
if_icmpge: jump if val2 >= val1
if_acmpeq: jump if reference val2 == reference
val1
if_acmpne: jump if reference val2 != reference
val1
Compound
conditional branch:
lookupswitch
<key1> : <label1>
<key2> : <label2>
<key3> : <label3>
...
<keyn> : <labeln>
default: <defaultLabel>
val1 is popped off the stack. If
it equals any of the key# values, the process jumps to
the <label#> location in the bytecode. Otherwise,
the process jumps to the location identified by <defaultLabel>.
Implements the switch/case
statement in the Java language.
tableswitch
<low>, <high>
<label1>
<label2>
<label3>
...
<labeln>
default: <defaultLabel>
val1 is popped off the stack. If
it less than <low> or greater than <high>
the process jumps to the location indicated by <defaultLabel>.
Otherwise, the process jumps to the <labelM> for
M=val1-low.
Unconditional
branch:
goto <label>
The process jumps to the address
given by the 16 bit offset value in <label>.
goto_w <label>
The process jumps to the address
given by the 32 bit offset value in <label>
jsr <label>
The pushes the current location
+ 3 onto the stack and the jumps to the address of a subroutine
given by the 16 bit offset value in <label> . The
subroutine pops the address from the stack and stores
it in a local variable. When it executes the ret
command, the address is obtained from the local variable
and the process goes to that location.
jsr_w <label>
Same as jsr
except it uses a 32 bit offset value in <label>.
ret <variable>
In a subroutine this operation
causes the process to jump to the address stored in the
local variable indicated by <variable>.
See Chapter 2: Basics
: Flow Control and Chapter
2: Basics : Conditional if-else Statements. See also
Chapter
2: Java : Operators : Comparison.
|
Load
& Store Instructions |
Push
a value from a variable onto the stack:
iload
<var>
lload <var>
fload <var>
dload <var>
aload <var>
Take the value in local variable
pointed to by index <var>
of the type indicated by the first letter of the instruction
and put it on top of the stack.
iload_<n>
lload_<n>
fload_<n>
dload_<n>
aload_<n>
Where n=(0,
1, 2 or 3), where n
indicates an index to a local variable. Obtain the value
in local variable n
of type indicated by the first letter of the instruction
and push it on top of the stack.
Load
a constant value onto the operand stack:
bipush
<byte>
Pushes a byte value onto
the stack (sign extends it to fill the 32 bit wide stack
word). The <byte> value follows the instruction
in the bytecode.
sipush
<short>
Pushes a 16 bit value
onto the stack (sign extends it to fill the 32 bit wide
stack word). Here the <short> value follows the
instruction in the bytecode.
ldc
<const>
Pushes a 32 bit constant
onto the stack. Can be an int,
float or
String
reference. Here the <const>
8 bit value follows the instruction in the bytecode and
is an index into the constants pool, which is an
array where the class stores constants (such as from literals),
strings and other data.
Ldc_w
<const>
Same as ldc
but the <const>
value is a 16 bit index into the constants pool.
ldc2_w
<const>
Pushes a 64 bit constant
onto the stack (as two 32 bit wide words). Can be an long
or double.
Here the <const>
16 bit value follows the instruction in the bytecode and
is an index into the constants pool, which is an
array where the class stores constants (such as from literals),
strings and other data.
iconst_<n>
Where n=(0,1,2,3,4
or 5) push the value n
of type int
onto the stack.
lconst_<n>
Where n=(0
or 1) push the value n
of type long
onto the stack.
fconst_<n>
Where n=(0,
1, or 2) push the value n
of type float
onto the stack ( 0.0, 1.0, or 2.0, resp.)
dconst_<n>
Where n=(0
or 1) push the value n
of type double
onto the stack ( 0.0 or 1.0, or 2.0, resp.)
iconst_m1
Push -1
onto the stack
aconst_null
Push null
reference onto the stack
Store
a value from the operand stack into a local variable:
istore
<var>
lstore
<var>
fstore
<var>
dstore
<var>
astore
<var>
Pop val1
off the stack and store in local variable indexed by <var>
of the type indicated by the first letter of the instruction.
istore_n
lstore_n
fstore_n
dstore_n
astore_n
For n
= (0,1,2,or 3), where n indicates
an index to the zeroth through third local variable.
Pop val1
off the stack and store in the nth local variable
of the type indicated by the first letter of the instruction.
Extend
index:
wide
When placed in front of these instructions
- aload,dload,iload,fload,lload,
astore,dstore,istore,fstore,lstore,ret - the index
to local variable is extended from 8 bits to 16 bits.
(Also, used with iinc
to extend its increment size to 16 bits.)
See assignments in Chapter
2: Java : Expressions, assignment operators in Chapter
2: Java : Operators, declarations in Chapter
2: Java : Statements.
|
Stack
Instructions |
pop
items off the stack:
pop
Discard the word currently on
top of the stack. Removes, for example, a single int
value.
pop2
Discard the two words currently
on top of the stack. Removes, for example, two int
values or one long
value.
duplicate
items on the stack:
dup
Duplicate the word currently
on top of the stack. So
val1 => val1
val1
Can't be used if
val1 is the high end of a 2 word
type, i.e. long
or double.
Must use dup2 in that case.
dup2
Duplicate the word currently
on top of the stack. So
val1 => val1
val2
val2
val1
val2
dup_x1
Duplicate the word on top of
the stack and insert the copy below the second word on
the stack:
val1 => val1
val2
val2
val1
Can't be used if
val1 or val2
are part of a 2 word type, i.e. long
or double.
Must use dup2_x1 in that case.
dup2_x1
Duplicate the 2 words on top
of the stack and insert the copy below the third word
on the stack:
val1 => val1
val2
val2
val3 val3
val1
val2
dup_x2
Duplicate the word on top of
the stack and insert the copy below the third word on
the stack:
val1 => val1
val2
val2
val3 val3
val1
Can't be used if
val1 is part of a 2 word type, i.e.
long or
double.
Must use dup2_x1 in that case.
dup2_x2
Duplicate the 2 words on top
of the stack and insert the copy below the fourth word
on the stack:
val1 => val1
val2
val2
val3 val3
val4 val4
val1
val2
Swap
values on the stack:
swap
Swaps the top two words on the stack:
val1
=> val2
val2
val1
|
References & Web Resources
Latest update: Oct. 4, 2005
|