I guess it is a basic question, but why there is no sload instruction? Why can you load all primitives besides short? (There is saload, but still...)
For :
public class ShortTest {
public void test() {
short i = 1;
System.out.print(i);
}
}
Compiler still uses iload_1. Is it because short is 16 bit type and processors handle better 32bits (since all modern processors are 32/64bits)?
Refer to the JVM specification, §2.11.1. Types and the Java Virtual Machine:
Note that most instructions in Table 2.11.1-A do not have forms for the integral types
byte
,char
, andshort
. None have forms for theboolean
type. A compiler encodes loads of literal values of typesbyte
andshort
using Java Virtual Machine instructions that sign-extend those values to values of typeint
at compile-time or run-time. Loads of literal values of typesboolean
andchar
are encoded using instructions that zero-extend the literal to a value of typeint
at compile-time or run-time. Likewise, loads from arrays of values of typeboolean
,byte
,short
, andchar
are encoded using Java Virtual Machine instructions that sign-extend or zero-extend the values to values of typeint
. Thus, most operations on values of actual typesboolean
,byte
,char
, andshort
are correctly performed by instructions operating on values of computational typeint
.
It’s worth recalling that in Java, any integer arithmetic not involving long
will have an int
result, regardless of whether the input is byte
, char
, short
, or int
.
So a line like
short i = 1, j = 2, k = i + j;
will not compile, but require a type cast, like
short i = 1, j = 2, k = (short)(i + j);
And this type cast will be the only indicator that short
is involved. Letting debug hints aside, there is no formal declaration of local variables in bytecode, but only assignments of values which determine their type. So local variables of type short
simply do not exist. The code above compiles to
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: i2s
8: istore_3
which is identical to the compiled form of
int i = 1, j = 2, k = (short)(i + j);
But mind that the compile-time type of variables can change which method the compiler chooses for an invocation in case of overloads. Which is especially important if the types carry different semantics, like in the case of print(boolean)
or print(char)
. While the value passed to the method has an int
type in either case, the outcome is entirely different.
Another example of differences enforced by the compiler is
{
int i = 1;
i++;
}
{
short s = 1;
s++;
}
which gets compiled to
0: iconst_1
1: istore_1
2: iinc 1, 1
5: iconst_1
6: istore_1
7: iload_1
8: iconst_1
9: iadd
10: i2s
11: istore_1
So, since the calculation is always performed in 32 bit, the compiler inserts the necessary code to truncate the result to short
for the second increment. Note again the absence of variable declarations, so the code is identical to the compiled form of
int i = 1;
i++;
i = 1;
i = (short)(i+1);
It’s also worth looking at the Verification Type System, as the verifier will check the validity of all transfers from and to local variables:
The type checker enforces a type system based upon a hierarchy of verification types, illustrated below.
Verification type hierarchy: top ____________/\____________ / \ / \ oneWord twoWord / | \ / \ / | \ / \ int float reference long double / \ / \_____________ / \ / \ uninitialized +------------------+ / \ | Java reference | / \ | type hierarchy | uninitializedThis uninitialized(Offset) +------------------+ | | null
So the type system is simplified, compared to the Java language types, and the verifier doesn’t mind, e.g. if you pass a boolean
value to a method expecting a char
, as both are int
types.