To index callers of a callee, our modified JIT compiler maintains a table of (callee, caller) pairs. In Java bytecode, the target of invokevirtual is only a symbolic reference to the name and descriptor of the method as well as a symbolic reference to the class where the method can be found. Resolving the method reference requires the class to be loaded first. A VM can delay method resolution until the call instruction is executed at runtime. Therefore, the real target may not be known at JIT compilation time.
To deal with lazy class resolution and polymorphism, our approach uses the callee's method name and descriptor in the table. For example, if both methods X.x() and Y.y() have virtual calls of a symbolic reference A.m(), and another method Z.z() has a virtual call of B.m(), our approach assumes that all three methods are possible callers of any method with the signature m()2, and allocates slots in the TIB for all of them. At runtime, only two CTB entries of A.m() may be filled, and only one entry of B.m() may get filled. With this solution no accuracy is lost, but some space may be wasted due to unfilled CTB entries. Although some space is sacrificed, our approach simplifies the task of handling symbolic references and polymorphism. In real applications we observed that only a few common method signatures, such as equals(java.lang.Object), and hashCode(), have large caller sets where space is unused.
Feng Qian 2004-02-16