implement lazy class/field/method resolution in JIT compiler

Unlike the interpreter, the JIT compiler tries to resolve all the
symbols referenced by a method when compiling that method.  However,
this can backfire if a symbol cannot be resolved: we end up throwing
an e.g. NoClassDefFoundError for code which may never be executed.
This is particularly troublesome for code which supports multiple
APIs, choosing one at runtime.

The solution is to defer to stub code for symbols which can't be
resolved at JIT compile time.  Such a stub will try again at runtime
to resolve the needed symbol and throw an appropriate error if it
still can't be found.
This commit is contained in:
Joel Dice 2011-03-15 17:52:02 -06:00
parent 48e569c65a
commit 453ceb42ab
9 changed files with 1508 additions and 540 deletions

View File

@ -15,6 +15,7 @@ public class VMField {
public byte code;
public short flags;
public short offset;
public short index;
public byte[] name;
public byte[] spec;
public FieldAddendum addendum;

File diff suppressed because it is too large Load Diff

View File

@ -1460,27 +1460,11 @@ interpret3(Thread* t, const int base)
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
and BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField)))
{
PROTECT(t, field);
acquire(t, field);
}
PROTECT(t, field);
ACQUIRE_FIELD_FOR_READ(t, field);
pushField(t, popObject(t), field);
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
loadMemoryBarrier();
}
}
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
goto throw_;
@ -1498,26 +1482,9 @@ interpret3(Thread* t, const int base)
if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke;
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
and BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField)))
{
acquire(t, field);
}
ACQUIRE_FIELD_FOR_READ(t, field);
pushField(t, classStaticTable(t, fieldClass(t, field)), field);
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
loadMemoryBarrier();
}
}
} goto loop;
case goto_: {
@ -2460,80 +2427,61 @@ interpret3(Thread* t, const int base)
assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0);
PROTECT(t, field);
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
acquire(t, field);
} else {
storeStoreMemoryBarrier();
}
}
{ ACQUIRE_FIELD_FOR_WRITE(t, field);
switch (fieldCode(t, field)) {
case ByteField:
case BooleanField:
case CharField:
case ShortField:
case FloatField:
case IntField: {
int32_t value = popInt(t);
object o = popObject(t);
if (LIKELY(o)) {
switch (fieldCode(t, field)) {
case ByteField:
case BooleanField:
cast<int8_t>(o, fieldOffset(t, field)) = value;
break;
switch (fieldCode(t, field)) {
case ByteField:
case BooleanField:
case CharField:
case ShortField:
case FloatField:
case IntField: {
int32_t value = popInt(t);
object o = popObject(t);
if (LIKELY(o)) {
switch (fieldCode(t, field)) {
case ByteField:
case BooleanField:
cast<int8_t>(o, fieldOffset(t, field)) = value;
break;
case CharField:
case ShortField:
cast<int16_t>(o, fieldOffset(t, field)) = value;
break;
case CharField:
case ShortField:
cast<int16_t>(o, fieldOffset(t, field)) = value;
break;
case FloatField:
case IntField:
cast<int32_t>(o, fieldOffset(t, field)) = value;
break;
case FloatField:
case IntField:
cast<int32_t>(o, fieldOffset(t, field)) = value;
break;
}
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} break;
} break;
case DoubleField:
case LongField: {
int64_t value = popLong(t);
object o = popObject(t);
if (LIKELY(o)) {
cast<int64_t>(o, fieldOffset(t, field)) = value;
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} break;
case DoubleField:
case LongField: {
int64_t value = popLong(t);
object o = popObject(t);
if (LIKELY(o)) {
cast<int64_t>(o, fieldOffset(t, field)) = value;
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} break;
case ObjectField: {
object value = popObject(t);
object o = popObject(t);
if (LIKELY(o)) {
set(t, o, fieldOffset(t, field), value);
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} break;
case ObjectField: {
object value = popObject(t);
object o = popObject(t);
if (LIKELY(o)) {
set(t, o, fieldOffset(t, field), value);
} else {
exception = makeThrowable(t, Machine::NullPointerExceptionType);
}
} break;
default: abort(t);
}
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
storeLoadMemoryBarrier();
default: abort(t);
}
}
@ -2551,16 +2499,7 @@ interpret3(Thread* t, const int base)
PROTECT(t, field);
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
acquire(t, field);
} else {
storeStoreMemoryBarrier();
}
}
ACQUIRE_FIELD_FOR_WRITE(t, field);
if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke;
@ -2603,17 +2542,6 @@ interpret3(Thread* t, const int base)
default: abort(t);
}
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
storeLoadMemoryBarrier();
}
}
} goto loop;
case ret: {

View File

@ -1075,6 +1075,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool)
code,
flags,
0, // offset
i,
singletonObject(t, pool, name - 1),
singletonObject(t, pool, spec - 1),
addendum,
@ -1270,20 +1271,6 @@ parseCode(Thread* t, Stream& s, object pool)
return code;
}
void
scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount,
unsigned* returnCode)
{
unsigned count = 0;
MethodSpecIterator it(t, s);
for (; it.hasNext(); it.next()) {
++ count;
}
*parameterCount = count;
*returnCode = fieldCode(t, *it.returnSpec());
}
object
addInterfaceMethods(Thread* t, object class_, object virtualMap,
unsigned* virtualCount, bool makeList)
@ -2225,6 +2212,17 @@ doCollect(Thread* t, Heap::CollectionType type)
}
}
uint64_t
invokeLoadClass(Thread* t, uintptr_t* arguments)
{
object method = reinterpret_cast<object>(arguments[0]);
object loader = reinterpret_cast<object>(arguments[1]);
object specString = reinterpret_cast<object>(arguments[2]);
return reinterpret_cast<uintptr_t>
(t->m->processor->invoke(t, method, loader, specString));
}
} // namespace
namespace vm {
@ -3342,8 +3340,6 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
if (objectClass(t, loader) == type(t, Machine::SystemClassLoaderType)) {
return resolveSystemClass(t, loader, spec, throw_);
} else {
expect(t, throw_);
PROTECT(t, loader);
PROTECT(t, spec);
@ -3384,9 +3380,23 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
object specString = makeString(t, "%s", RUNTIME_ARRAY_BODY(s));
object jc = t->m->processor->invoke(t, method, loader, specString);
uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(method),
reinterpret_cast<uintptr_t>(loader),
reinterpret_cast<uintptr_t>(specString) };
object jc = reinterpret_cast<object>
(runRaw(t, invokeLoadClass, arguments));
if (LIKELY(jc)) {
c = jclassVmClass(t, jc);
} else if (t->exception) {
if (throw_) {
object e = t->exception;
t->exception = 0;
vm::throw_(t, e);
} else {
t->exception = 0;
}
}
}
@ -3402,12 +3412,12 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
hashMapInsert
(t, classLoaderMap(t, loader), spec, c, byteArrayHash);
return c;
} else {
} else if (throw_) {
throwNew(t, Machine::ClassNotFoundExceptionType, "%s",
&byteArrayBody(t, spec, 0));
}
return c;
}
}

View File

@ -33,6 +33,12 @@
#define ACQUIRE_OBJECT(t, x) \
ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x)
#define ACQUIRE_FIELD_FOR_READ(t, field) \
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
#define ACQUIRE_FIELD_FOR_WRITE(t, field) \
FieldReadResource MAKE_NAME(monitorResource_) (t, field)
#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x)
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
@ -2430,6 +2436,20 @@ fieldSize(Thread* t, object field)
return fieldSize(t, fieldCode(t, field));
}
inline void
scanMethodSpec(Thread* t, const char* s, unsigned* parameterCount,
unsigned* returnCode)
{
unsigned count = 0;
MethodSpecIterator it(t, s);
for (; it.hasNext(); it.next()) {
++ count;
}
*parameterCount = count;
*returnCode = fieldCode(t, *it.returnSpec());
}
object
findLoadedClass(Thread* t, object loader, object spec);
@ -2639,11 +2659,11 @@ findInHierarchyOrNull(Thread* t, object class_, object name, object spec,
inline object
findInHierarchy(Thread* t, object class_, object name, object spec,
object (*find)(Thread*, object, object, object),
Machine::Type errorType)
Machine::Type errorType, bool throw_ = true)
{
object o = findInHierarchyOrNull(t, class_, name, spec, find);
if (o == 0) {
if (throw_ and o == 0) {
throwNew(t, errorType, "%s %s not found in %s",
&byteArrayBody(t, name, 0),
&byteArrayBody(t, spec, 0),
@ -3332,93 +3352,208 @@ poolSize(Thread* t, object pool)
inline object
resolveClassInObject(Thread* t, object loader, object container,
unsigned classOffset)
unsigned classOffset, bool throw_ = true)
{
object o = cast<object>(container, classOffset);
loadMemoryBarrier();
if (objectClass(t, o) == type(t, Machine::ByteArrayType)) {
PROTECT(t, container);
o = resolveClass(t, loader, o);
o = resolveClass(t, loader, o, throw_);
set(t, container, classOffset, o);
if (o) {
storeStoreMemoryBarrier();
set(t, container, classOffset, o);
}
}
return o;
}
inline object
resolveClassInPool(Thread* t, object loader, object method, unsigned index)
resolveClassInPool(Thread* t, object loader, object method, unsigned index,
bool throw_ = true)
{
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
loadMemoryBarrier();
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
PROTECT(t, method);
o = resolveClass(t, loader, referenceName(t, o));
o = resolveClass(t, loader, referenceName(t, o), throw_);
set(t, codePool(t, methodCode(t, method)),
SingletonBody + (index * BytesPerWord), o);
if (o) {
storeStoreMemoryBarrier();
set(t, codePool(t, methodCode(t, method)),
SingletonBody + (index * BytesPerWord), o);
}
}
return o;
}
inline object
resolveClassInPool(Thread* t, object method, unsigned index)
resolveClassInPool(Thread* t, object method, unsigned index,
bool throw_ = true)
{
return resolveClassInPool(t, classLoader(t, methodClass(t, method)),
method, index);
method, index, throw_);
}
inline object
resolve(Thread* t, object loader, object method, unsigned index,
object (*find)(vm::Thread*, object, object, object),
Machine::Type errorType)
Machine::Type errorType, bool throw_ = true)
{
object o = singletonObject(t, codePool(t, methodCode(t, method)), index);
if (objectClass(t, o) == type(t, Machine::ReferenceType))
{
loadMemoryBarrier();
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
PROTECT(t, method);
object reference = o;
PROTECT(t, reference);
object class_ = resolveClassInObject(t, loader, o, ReferenceClass);
object class_ = resolveClassInObject(t, loader, o, ReferenceClass, throw_);
o = findInHierarchy
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
find, errorType);
if (class_) {
o = findInHierarchy
(t, class_, referenceName(t, reference), referenceSpec(t, reference),
find, errorType, throw_);
set(t, codePool(t, methodCode(t, method)),
SingletonBody + (index * BytesPerWord), o);
if (o) {
storeStoreMemoryBarrier();
set(t, codePool(t, methodCode(t, method)),
SingletonBody + (index * BytesPerWord), o);
}
} else {
o = 0;
}
}
return o;
}
inline object
resolveField(Thread* t, object loader, object method, unsigned index)
resolveField(Thread* t, object loader, object method, unsigned index,
bool throw_ = true)
{
return resolve(t, loader, method, index, findFieldInClass,
Machine::NoSuchFieldErrorType);
Machine::NoSuchFieldErrorType, throw_);
}
inline object
resolveField(Thread* t, object method, unsigned index)
resolveField(Thread* t, object method, unsigned index, bool throw_ = true)
{
return resolveField
(t, classLoader(t, methodClass(t, method)), method, index);
(t, classLoader(t, methodClass(t, method)), method, index, throw_);
}
inline void
acquireFieldForRead(Thread* t, object field)
{
if (UNLIKELY((fieldFlags(t, field) & ACC_VOLATILE)
and BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField)))
{
acquire(t, field);
}
}
inline void
releaseFieldForRead(Thread* t, object field)
{
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
loadMemoryBarrier();
}
}
}
class FieldReadResource {
public:
FieldReadResource(Thread* t, object o): o(o), protector(t, &(this->o)) {
acquireFieldForRead(protector.t, o);
}
~FieldReadResource() {
releaseFieldForRead(protector.t, o);
}
private:
object o;
Thread::SingleProtector protector;
};
inline void
acquireFieldForWrite(Thread* t, object field)
{
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
acquire(t, field);
} else {
storeStoreMemoryBarrier();
}
}
}
inline void
releaseFieldForWrite(Thread* t, object field)
{
if (UNLIKELY(fieldFlags(t, field) & ACC_VOLATILE)) {
if (BytesPerWord == 4
and (fieldCode(t, field) == DoubleField
or fieldCode(t, field) == LongField))
{
release(t, field);
} else {
storeLoadMemoryBarrier();
}
}
}
class FieldWriteResource {
public:
FieldWriteResource(Thread* t, object o): o(o), protector(t, &(this->o)) {
acquireFieldForWrite(protector.t, o);
}
~FieldWriteResource() {
releaseFieldForWrite(protector.t, o);
}
private:
object o;
Thread::SingleProtector protector;
};
inline object
resolveMethod(Thread* t, object loader, object method, unsigned index)
resolveMethod(Thread* t, object loader, object method, unsigned index,
bool throw_ = true)
{
return resolve(t, loader, method, index, findMethodInClass,
Machine::NoSuchMethodErrorType);
Machine::NoSuchMethodErrorType, throw_);
}
inline object
resolveMethod(Thread* t, object method, unsigned index)
resolveMethod(Thread* t, object method, unsigned index, bool throw_ = true)
{
return resolveMethod
(t, classLoader(t, methodClass(t, method)), method, index);
(t, classLoader(t, methodClass(t, method)), method, index, throw_);
}
object

View File

@ -1,5 +1,9 @@
THUNK(tryInitClass)
THUNK(findInterfaceMethodFromInstance)
THUNK(findInterfaceMethodFromInstanceAndReference)
THUNK(findSpecialMethodFromReference)
THUNK(findStaticMethodFromReference)
THUNK(findVirtualMethodFromReference)
THUNK(compareDoublesG)
THUNK(compareDoublesL)
THUNK(compareFloatsG)
@ -36,17 +40,31 @@ THUNK(intToFloat)
THUNK(longToDouble)
THUNK(longToFloat)
THUNK(makeBlankObjectArray)
THUNK(makeBlankObjectArrayFromReference)
THUNK(makeBlankArray)
THUNK(lookUpAddress)
THUNK(setMaybeNull)
THUNK(acquireMonitorForObject)
THUNK(releaseMonitorForObject)
THUNK(makeMultidimensionalArray)
THUNK(makeMultidimensionalArrayFromReference)
THUNK(throw_)
THUNK(checkCast)
THUNK(checkCastFromReference)
THUNK(getStaticFieldValueFromReference)
THUNK(getFieldValueFromReference)
THUNK(setStaticFieldValueFromReference)
THUNK(setFieldValueFromReference)
THUNK(setStaticLongFieldValueFromReference)
THUNK(setLongFieldValueFromReference)
THUNK(setStaticObjectFieldValueFromReference)
THUNK(setObjectFieldValueFromReference)
THUNK(instanceOf64)
THUNK(instanceOfFromReference)
THUNK(makeNewGeneral64)
THUNK(makeNew64)
THUNK(makeNewFromReference)
THUNK(set)
THUNK(getJClass64)
THUNK(getJClassFromReference)
THUNK(gcIfNecessary)

View File

@ -33,6 +33,7 @@ class Vector {
void dispose() {
if (data and minimumCapacity >= 0) {
allocator->free(data, capacity);
data = 0;
}
}

View File

@ -45,6 +45,8 @@ class Zone: public Allocator {
next = seg->next;
allocator->free(seg, sizeof(Segment) + seg->size);
}
segment = 0;
}
bool ensure(unsigned space) {

184
test/LazyLoading.java Normal file
View File

@ -0,0 +1,184 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class LazyLoading {
private static boolean loadLazy;
private static void expect(boolean v) {
if (! v) throw new RuntimeException();
}
private static File findClass(String name, File directory) {
for (File file: directory.listFiles()) {
if (file.isFile()) {
if (file.getName().equals(name + ".class")) {
return file;
}
} else if (file.isDirectory()) {
File result = findClass(name, file);
if (result != null) {
return result;
}
}
}
return null;
}
private static byte[] read(File file) throws IOException {
byte[] bytes = new byte[(int) file.length()];
FileInputStream in = new FileInputStream(file);
try {
if (in.read(bytes) != (int) file.length()) {
throw new RuntimeException();
}
return bytes;
} finally {
in.close();
}
}
public static void main(String[] args) throws Exception {
Class c = new MyClassLoader(LazyLoading.class.getClassLoader()).loadClass
("LazyLoading$Test");
c.getMethod("test").invoke(null);
}
private static class MyClassLoader extends ClassLoader {
public MyClassLoader(ClassLoader parent) {
super(parent);
}
protected Class findClass(String name) throws ClassNotFoundException {
try {
return defineClass
(name, read
(LazyLoading.findClass
(name, new File(System.getProperty("user.dir")))));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Class loadClass(String name) throws ClassNotFoundException {
if ("LazyLoading$Test".equals(name)) {
return findClass(name);
} else if ("LazyLoading$Lazy".equals(name)
|| "LazyLoading$Interface".equals(name))
{
if (loadLazy) {
return findClass(name);
} else {
throw new ClassNotFoundException();
}
} else {
return super.loadClass(name);
}
}
private Class defineClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
private static class Test {
public static void test() {
doTest();
loadLazy = true;
doTest();
}
private static void doTest() {
if (loadLazy) {
// anewarray
Lazy[] array = new Lazy[1];
// new and invokespecial
Object lazy = new Lazy();
// checkcast
array[0] = (Lazy) lazy;
// instanceof
expect(lazy instanceof Lazy);
// invokeinterface
Interface i = array[0];
expect(i.interfaceMethod() == 42);
// invokestatic
expect(Lazy.staticMethod() == 43);
// invokevirtual
expect(array[0].virtualMethod() == 44);
// ldc
expect(Lazy.class == lazy.getClass());
// multianewarray
Lazy[][] multiarray = new Lazy[5][6];
multiarray[2][3] = array[0];
expect(multiarray[2][3] == array[0]);
// getfield
expect(array[0].intField == 45);
// getstatic
expect(Lazy.intStaticField == 46);
// putfield int
array[0].intField = 47;
expect(array[0].intField == 47);
// putfield long
array[0].longField = 48;
expect(array[0].longField == 48);
// putfield object
Object x = new Object();
array[0].objectField = x;
expect(array[0].objectField == x);
// putstatic int
array[0].intStaticField = 49;
expect(array[0].intStaticField == 49);
// putstatic long
array[0].longStaticField = 50;
expect(array[0].longStaticField == 50);
// putstatic object
Object y = new Object();
array[0].objectStaticField = y;
expect(array[0].objectStaticField == y);
}
}
}
private interface Interface {
public int interfaceMethod();
}
private static class Lazy implements Interface {
public static int intStaticField = 46;
public static long longStaticField;
public static Object objectStaticField;
public int intField = 45;
public long longField;
public Object objectField;
public int interfaceMethod() {
return 42;
}
public static int staticMethod() {
return 43;
}
public int virtualMethod() {
return 44;
}
}
}