Merge pull request #454 from dicej/aot-lambda

support AOT-compilation of Java 8 lambda expressions
This commit is contained in:
Joshua Warner 2015-09-28 07:44:51 -06:00
commit d906db633c
22 changed files with 944 additions and 513 deletions

View File

@ -168,8 +168,8 @@ Library" below for details.
These flags determine the name of the directory used for the build.
The name always starts with _${platform}-${arch}_, and each non-default
build option is appended to the name. For example, a debug build with
bootimage enabled on Linux/i386 would be built in
_build/linux-i386-debug-bootimage_. This allows you to build with
bootimage enabled on Linux/x86_64 would be built in
_build/linux-x86_64-debug-bootimage_. This allows you to build with
several different sets of options independently and even
simultaneously without doing a clean build each time.
@ -575,7 +575,7 @@ For boot image builds:
Note you can use ProGuard without using a boot image and vice-versa,
as desired.
The following instructions assume we are building for Linux/i386.
The following instructions assume we are building for Linux/x86_64.
Please refer to the previous example for guidance on other platforms.
__1.__ Build Avian, create a new directory, and populate it with the
@ -584,13 +584,13 @@ VM object files.
$ make bootimage=true
$ mkdir hello
$ cd hello
$ ar x ../build/linux-i386-bootimage/libavian.a
$ ar x ../build/linux-x86_64-bootimage/libavian.a
__2.__ Create a stage1 directory and extract the contents of the
class library jar into it.
$ mkdir stage1
$ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar)
$ (cd stage1 && jar xf ../../build/linux-x86_64-bootimage/classpath.jar)
__3.__ Build the Java code and add it to stage1.
@ -630,10 +630,11 @@ using the OpenJDK library.)
__6.__ Build the boot and code images.
$ ../build/linux-i386-bootimage/bootimage-generator \
$ ../build/linux-x86_64-bootimage/bootimage-generator \
-cp stage2 \
-bootimage bootimage-bin.o \
-codeimage codeimage-bin.o
-codeimage codeimage-bin.o \
-hostvm ../build/linux-x86_64-interpret/libjvm.so
Note that you can override the default names for the start and end
symbols in the boot/code image by also passing:

View File

@ -423,6 +423,27 @@ public class Classes {
}
}
public static VMMethod findMethod(ClassLoader loader,
String class_,
String name,
String spec)
throws ClassNotFoundException
{
VMClass c = SystemClassLoader.vmClass(loader.loadClass(class_));
VMMethod[] methodTable = c.methodTable;
if (methodTable != null) {
link(c);
for (int i = 0; i < methodTable.length; ++i) {
VMMethod m = methodTable[i];
if (toString(m.name).equals(name) && toString(m.spec).equals(spec)) {
return m;
}
}
}
return null;
}
public static int findMethod(VMClass vmClass, String name,
Class[] parameterTypes)
{

View File

@ -20,6 +20,8 @@ import java.util.Enumeration;
import java.util.NoSuchElementException;
public class SystemClassLoader extends ClassLoader {
public static native ClassLoader appLoader();
private native VMClass findVMClass(String name)
throws ClassNotFoundException;

View File

@ -15,7 +15,10 @@ import java.util.Random;
public final class Math {
public static final double E = 2.718281828459045;
public static final double PI = 3.141592653589793;
private static final Random random = new Random();
private static class Static {
public static final Random random = new Random();
}
private Math() { }
@ -84,7 +87,7 @@ public final class Math {
}
public static double random() {
return random.nextDouble();
return Static.random.nextDouble();
}
public static native double floor(double v);

View File

@ -22,7 +22,9 @@ import java.util.Hashtable;
import java.util.Properties;
public abstract class System {
private static final long NanoTimeBaseInMillis = currentTimeMillis();
private static class NanoTime {
public static final long BaseInMillis = currentTimeMillis();
}
private static class Static {
public static Properties properties = makeProperties();
@ -94,7 +96,7 @@ public abstract class System {
public static native int identityHashCode(Object o);
public static long nanoTime() {
return (currentTimeMillis() - NanoTimeBaseInMillis) * 1000000;
return (currentTimeMillis() - NanoTime.BaseInMillis) * 1000000;
}
public static String mapLibraryName(String name) {

View File

@ -188,13 +188,27 @@ public class LambdaMetafactory {
return result;
}
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
public static byte[] makeLambda(String invokedName,
String invokedType,
String methodType,
String implementationClass,
String implementationName,
String implementationSpec,
int implementationKind)
{
return makeLambda(invokedName,
new MethodType(invokedType),
new MethodType(methodType),
new MethodHandle(implementationClass,
implementationName,
implementationSpec,
implementationKind));
}
private static byte[] makeLambda(String invokedName,
MethodType invokedType,
MethodType methodType,
MethodHandle methodImplementation,
MethodType instantiatedMethodType)
throws LambdaConversionException
MethodHandle methodImplementation)
{
String className;
{ int number;
@ -265,7 +279,18 @@ public class LambdaMetafactory {
throw error;
}
byte[] classData = out.toByteArray();
return out.toByteArray();
}
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType methodType,
MethodHandle methodImplementation,
MethodType instantiatedMethodType)
throws LambdaConversionException
{
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation);
try {
return new CallSite

View File

@ -1,6 +1,7 @@
package java.lang.invoke;
import avian.Classes;
import avian.SystemClassLoader;
public class MethodHandle {
static final int REF_invokeStatic = 6;
@ -17,6 +18,20 @@ public class MethodHandle {
this.method = method;
}
MethodHandle(String class_,
String name,
String spec,
int kind)
{
this.kind = kind;
this.loader = SystemClassLoader.appLoader();
try {
this.method = Classes.findMethod(this.loader, class_, name, spec);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (method.class_ != null) {

View File

@ -4,6 +4,7 @@ import static avian.Assembler.*;
import avian.Assembler;
import avian.Classes;
import avian.SystemClassLoader;
import avian.VMClass;
import java.util.List;
@ -25,6 +26,12 @@ public final class MethodType implements java.io.Serializable {
this.spec = spec;
}
MethodType(String spec) {
this.loader = SystemClassLoader.appLoader();
this.spec = new byte[spec.length() + 1];
spec.getBytes(0, spec.length(), this.spec, 0);
}
public String toMethodDescriptorString() {
return Classes.makeString(spec, 0, spec.length - 1);
}

View File

@ -176,7 +176,7 @@ inline void NO_RETURN sysAbort(System* s)
// #endif // not NDEBUG
AVIAN_EXPORT System* makeSystem();
AVIAN_EXPORT System* makeSystem(bool reentrant = false);
} // namespace vm

View File

@ -1333,6 +1333,12 @@ bootimage-generator-objects = \
$(call cpp-objects,$(bootimage-generator-sources),$(src),$(build))
bootimage-generator = $(build)/bootimage-generator
ifneq ($(mode),fast)
host-vm-options := -$(mode)
endif
host-vm = build/$(build-platform)-$(build-arch)-interpret$(host-vm-options)/libjvm.so
bootimage-object = $(build)/bootimage-bin.o
codeimage-object = $(build)/codeimage-bin.o
@ -2064,7 +2070,8 @@ $(bootimage-object) $(codeimage-object): $(bootimage-generator) \
@echo "generating bootimage and codeimage binaries from $(classpath-build) using $(<)"
$(<) -cp $(classpath-build) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \
-bootimage-symbols $(bootimage-symbols) \
-codeimage-symbols $(codeimage-symbols)
-codeimage-symbols $(codeimage-symbols) \
-hostvm $(host-vm)
executable-objects = $(vm-objects) $(classpath-objects) $(driver-object) \
$(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \
@ -2119,6 +2126,7 @@ $(unittest-executable): $(unittest-executable-objects)
$(bootimage-generator): $(bootimage-generator-objects) $(vm-objects)
echo building $(bootimage-generator) arch=$(build-arch) platform=$(bootimage-platform)
$(MAKE) process=interpret bootimage= mode=$(mode)
$(MAKE) mode=$(mode) \
build=$(host-build-root) \
arch=$(build-arch) \

View File

@ -55,10 +55,13 @@ class BootImage {
} PACKED;
class GcField;
class GcClass;
class OffsetResolver {
public:
virtual unsigned fieldOffset(Thread*, GcField*) = 0;
virtual void addClass(Thread*, GcClass*, const uint8_t*, size_t) = 0;
};
#define NAME(x) Target##x

View File

@ -20,6 +20,7 @@
#define EMBED_PREFIX_PROPERTY "avian.embed.prefix"
#define CLASSPATH_PROPERTY "java.class.path"
#define JAVA_HOME_PROPERTY "java.home"
#define REENTRANT_PROPERTY "avian.reentrant"
#define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p"
#define BOOTCLASSPATH_OPTION "bootclasspath"
#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a"

View File

@ -198,6 +198,14 @@ const unsigned ConstructorFlag = 1 << 1;
#define JNI_VERSION_1_6 0x00010006
#endif
#ifndef JNI_TRUE
#define JNI_TRUE 1
#endif
#ifndef JNI_OK
#define JNI_OK 0
#endif
typedef Machine JavaVM;
typedef Thread JNIEnv;
@ -207,6 +215,19 @@ struct JNINativeMethod {
void* function;
};
struct JavaVMOption {
char* optionString;
void* extraInfo;
};
struct JavaVMInitArgs {
jint version;
jint nOptions;
JavaVMOption* options;
jboolean ignoreUnrecognized;
};
struct JavaVMVTable {
void* reserved0;
void* reserved1;
@ -3737,7 +3758,7 @@ void populateMultiArray(Thread* t,
GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false);
object defineClass(Thread* t,
GcClass* defineClass(Thread* t,
GcClassLoader* loader,
const uint8_t* buffer,
unsigned length);

View File

@ -170,7 +170,8 @@ class Processor {
GcTriple** calls,
avian::codegen::DelayedPromise** addresses,
GcMethod* method,
OffsetResolver* resolver) = 0;
OffsetResolver* resolver,
Machine* hostVM) = 0;
virtual void visitRoots(Thread* t, HeapWalker* w) = 0;

View File

@ -183,6 +183,12 @@ extern "C" AVIAN_EXPORT int64_t JNICALL
t->m->classpath->makeString(t, array, offset, length));
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_avian_SystemClassLoader_appLoader(Thread* t, object, uintptr_t*)
{
return reinterpret_cast<int64_t>(roots(t)->appLoader());
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_avian_SystemClassLoader_findLoadedVMClass(Thread* t,
object,

View File

@ -909,14 +909,16 @@ class BootContext {
GcTriple* calls,
avian::codegen::DelayedPromise* addresses,
Zone* zone,
OffsetResolver* resolver)
OffsetResolver* resolver,
JavaVM* hostVM)
: protector(t, this),
constants(constants),
calls(calls),
addresses(addresses),
addressSentinal(addresses),
zone(zone),
resolver(resolver)
resolver(resolver),
hostVM(hostVM)
{
}
@ -927,6 +929,7 @@ class BootContext {
avian::codegen::DelayedPromise* addressSentinal;
Zone* zone;
OffsetResolver* resolver;
JavaVM* hostVM;
};
class Context {
@ -3991,6 +3994,34 @@ void checkField(Thread* t, GcField* field, bool shouldBeStatic)
}
}
bool isLambda(Thread* t,
GcClassLoader* loader,
GcCharArray* bootstrapArray,
GcInvocation* invocation)
{
GcMethod* bootstrap = cast<GcMethod>(t,
resolve(t,
loader,
invocation->pool(),
bootstrapArray->body()[0],
findMethodInClass,
GcNoSuchMethodError::Type));
PROTECT(t, bootstrap);
return vm::strcmp(reinterpret_cast<const int8_t*>(
"java/lang/invoke/LambdaMetafactory"),
bootstrap->class_()->name()->body().begin()) == 0
and vm::strcmp(reinterpret_cast<const int8_t*>("metafactory"),
bootstrap->name()->body().begin()) == 0
and vm::strcmp(
reinterpret_cast<const int8_t*>(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/"
"String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/"
"MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/"
"invoke/MethodType;)Ljava/lang/invoke/CallSite;"),
bootstrap->spec()->body().begin()) == 0;
}
void compile(MyThread* t,
Frame* initialFrame,
unsigned initialIp,
@ -5054,6 +5085,132 @@ loop:
invocation->setClass(t, context->method->class_());
BootContext* bc = context->bootContext;
if (bc) {
// When we're AOT-compiling an application, we can't handle
// invokedynamic in general, since it usually implies runtime
// code generation. However, Java 8 lambda expressions are a
// special case for which we can generate code ahead of time.
//
// The only tricky part about it is that the class synthesis
// code resides in LambdaMetaFactory, which means we need to
// call out to a separate Java VM to execute it (the VM we're
// currently executing in won't work because it only knows how
// to compile code for the target machine, which might not be
// the same as the host; plus we don't want to pollute the
// runtime heap image with stuff that's only needed at compile
// time).
GcClass* c = context->method->class_();
PROTECT(t, c);
GcCharArray* bootstrapArray = cast<GcCharArray>(
t,
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
->body()[invocation->bootstrap()]);
PROTECT(t, bootstrapArray);
if (isLambda(t, c->loader(), bootstrapArray, invocation)) {
JNIEnv* e;
if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) == 0) {
e->vtable->PushLocalFrame(e, 256);
jclass lmfClass
= e->vtable->FindClass(e, "java/lang/invoke/LambdaMetafactory");
jmethodID makeLambda = e->vtable->GetStaticMethodID(
e,
lmfClass,
"makeLambda",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/"
"lang/String;Ljava/lang/String;Ljava/lang/String;I)[B");
GcReference* reference = cast<GcReference>(
t,
singletonObject(
t, invocation->pool(), bootstrapArray->body()[2]));
int kind = reference->kind();
GcMethod* method
= cast<GcMethod>(t,
resolve(t,
c->loader(),
invocation->pool(),
bootstrapArray->body()[2],
findMethodInClass,
GcNoSuchMethodError::Type));
jarray lambda = e->vtable->CallStaticObjectMethod(
e,
lmfClass,
makeLambda,
e->vtable->NewStringUTF(
e,
reinterpret_cast<const char*>(
invocation->template_()->name()->body().begin())),
e->vtable->NewStringUTF(
e,
reinterpret_cast<const char*>(
invocation->template_()->spec()->body().begin())),
e->vtable->NewStringUTF(
e,
reinterpret_cast<const char*>(
cast<GcByteArray>(
t,
singletonObject(t,
invocation->pool(),
bootstrapArray->body()[1]))
->body()
.begin())),
e->vtable->NewStringUTF(
e,
reinterpret_cast<const char*>(
method->class_()->name()->body().begin())),
e->vtable->NewStringUTF(e,
reinterpret_cast<const char*>(
method->name()->body().begin())),
e->vtable->NewStringUTF(e,
reinterpret_cast<const char*>(
method->spec()->body().begin())),
kind);
uint8_t* bytes = reinterpret_cast<uint8_t*>(
e->vtable->GetPrimitiveArrayCritical(e, lambda, 0));
GcClass* lambdaClass
= defineClass(t,
roots(t)->appLoader(),
bytes,
e->vtable->GetArrayLength(e, lambda));
bc->resolver->addClass(
t, lambdaClass, bytes, e->vtable->GetArrayLength(e, lambda));
e->vtable->ReleasePrimitiveArrayCritical(e, lambda, bytes, 0);
e->vtable->PopLocalFrame(e, 0);
THREAD_RUNTIME_ARRAY(
t, char, spec, invocation->template_()->spec()->length());
memcpy(RUNTIME_ARRAY_BODY(spec),
invocation->template_()->spec()->body().begin(),
invocation->template_()->spec()->length());
GcMethod* target = resolveMethod(
t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec));
bool tailCall = isTailCall(t, code, ip, context->method, target);
compileDirectInvoke(t, frame, target, tailCall);
} else {
throwNew(
t, GcVirtualMachineError::Type, "unable to attach to host VM");
}
} else {
throwNew(t,
GcVirtualMachineError::Type,
"invokedynamic not supported for AOT-compiled code except "
"in the case of lambda expressions");
}
} else {
unsigned index = addDynamic(t, invocation);
GcMethod* template_ = invocation->template_();
@ -5062,21 +5219,26 @@ loop:
unsigned parameterFootprint = template_->parameterFootprint();
// TODO: can we allow tailCalls in general?
// e.g. what happens if the call site is later bound to a method that can't be tail called?
// e.g. what happens if the call site is later bound to a method that
// can't be tail called?
// NOTE: calling isTailCall right now would cause an segfault, since
// invocation->template_()->class_() will be null.
// bool tailCall
// = isTailCall(t, code, ip, context->method, invocation->template_());
// = isTailCall(t, code, ip, context->method,
// invocation->template_());
bool tailCall = false;
// todo: do we need to tell the compiler to add a load barrier
// here for VolatileCallSite instances?
ir::Value* result = c->stackCall(
c->memory(c->memory(c->threadRegister(), ir::Type::object(),
ir::Value* result
= c->stackCall(c->memory(c->memory(c->threadRegister(),
ir::Type::object(),
TARGET_THREAD_DYNAMICTABLE),
ir::Type::object(), index * TargetBytesPerWord),
tailCall ? Compiler::TailJump : 0, frame->trace(0, 0),
ir::Type::object(),
index * TargetBytesPerWord),
tailCall ? Compiler::TailJump : 0,
frame->trace(0, 0),
operandTypeForFieldCode(t, returnCode),
frame->peekMethodArguments(parameterFootprint));
@ -5085,6 +5247,7 @@ loop:
if (rSize) {
frame->pushReturnValue(returnCode, result);
}
}
} break;
case invokeinterface: {
@ -9134,10 +9297,12 @@ class MyProcessor : public Processor {
GcTriple** calls,
avian::codegen::DelayedPromise** addresses,
GcMethod* method,
OffsetResolver* resolver)
OffsetResolver* resolver,
JavaVM* hostVM)
{
MyThread* t = static_cast<MyThread*>(vmt);
BootContext bootContext(t, *constants, *calls, *addresses, zone, resolver);
BootContext bootContext(
t, *constants, *calls, *addresses, zone, resolver, hostVM);
compile(t, &codeAllocator, &bootContext, method);

View File

@ -3512,7 +3512,8 @@ class MyProcessor : public Processor {
GcTriple**,
avian::codegen::DelayedPromise**,
GcMethod*,
OffsetResolver*)
OffsetResolver*,
JavaVM*)
{
abort(s);
}

View File

@ -3617,6 +3617,7 @@ extern "C" AVIAN_EXPORT jint JNICALL
const char* bootLibraries = 0;
const char* classpath = 0;
const char* javaHome = AVIAN_JAVA_HOME;
bool reentrant = false;
const char* embedPrefix = AVIAN_EMBED_PREFIX;
const char* bootClasspathPrepend = "";
const char* bootClasspath = 0;
@ -3667,6 +3668,9 @@ extern "C" AVIAN_EXPORT jint JNICALL
} else if (strncmp(p, JAVA_HOME_PROPERTY "=", sizeof(JAVA_HOME_PROPERTY))
== 0) {
javaHome = p + sizeof(JAVA_HOME_PROPERTY);
} else if (strncmp(p, REENTRANT_PROPERTY "=", sizeof(REENTRANT_PROPERTY))
== 0) {
reentrant = strcmp(p + sizeof(REENTRANT_PROPERTY), "true") == 0;
} else if (strncmp(p,
EMBED_PREFIX_PROPERTY "=",
sizeof(EMBED_PREFIX_PROPERTY)) == 0) {
@ -3689,7 +3693,7 @@ extern "C" AVIAN_EXPORT jint JNICALL
++propertyCount;
}
System* s = makeSystem();
System* s = makeSystem(reentrant);
Heap* h = makeHeap(s, heapLimit);
Classpath* c = makeClasspath(s, h, javaHome, embedPrefix);

View File

@ -5889,14 +5889,14 @@ GcMethod* getCaller(Thread* t, unsigned target, bool skipMethodInvoke)
return v.method;
}
object defineClass(Thread* t,
GcClass* defineClass(Thread* t,
GcClassLoader* loader,
const uint8_t* buffer,
unsigned length)
{
PROTECT(t, loader);
object c = parseClass(t, loader, buffer, length);
GcClass* c = parseClass(t, loader, buffer, length);
// char name[byteArrayLength(t, className(t, c))];
// memcpy(name, &byteArrayBody(t, className(t, c), 0),
@ -5915,7 +5915,7 @@ object defineClass(Thread* t,
PROTECT(t, c);
saveLoadedClass(t, loader, cast<GcClass>(t, c));
saveLoadedClass(t, loader, c);
return c;
}

View File

@ -92,7 +92,7 @@ const int signals[] = {VisitSignal, InterruptSignal, PipeSignal};
const unsigned SignalCount = 3;
class MySystem;
MySystem* system;
MySystem* globalSystem;
void handleSignal(int signal, siginfo_t* info, void* context);
@ -624,10 +624,11 @@ class MySystem : public System {
System::Library* next_;
};
MySystem() : threadVisitor(0), visitTarget(0)
MySystem(bool reentrant) : reentrant(reentrant), threadVisitor(0), visitTarget(0)
{
expect(this, system == 0);
system = this;
if (not reentrant) {
expect(this, globalSystem == 0);
globalSystem = this;
expect(this, registerHandler(InterruptSignalIndex));
expect(this, registerHandler(VisitSignalIndex));
@ -635,6 +636,7 @@ class MySystem : public System {
expect(this, make(&visitLock) == 0);
}
}
// Returns true on success, false on failure
bool unregisterHandler(int index)
@ -708,6 +710,8 @@ class MySystem : public System {
System::Thread* sTarget,
ThreadVisitor* visitor)
{
expect(this, not reentrant);
assertT(this, st != sTarget);
Thread* target = static_cast<Thread*>(sTarget);
@ -767,7 +771,7 @@ class MySystem : public System {
threadVisitor = 0;
system->visitLock->notifyAll(t);
globalSystem->visitLock->notifyAll(t);
return result;
#endif // not __APPLE__
@ -938,18 +942,21 @@ class MySystem : public System {
virtual void dispose()
{
if (not reentrant) {
visitLock->dispose();
expect(this, unregisterHandler(InterruptSignalIndex));
expect(this, unregisterHandler(VisitSignalIndex));
expect(this, unregisterHandler(PipeSignalIndex));
system = 0;
globalSystem = 0;
}
::free(this);
}
struct sigaction oldHandlers[SignalCount];
bool reentrant;
ThreadVisitor* threadVisitor;
Thread* visitTarget;
System::Monitor* visitLock;
@ -965,13 +972,13 @@ void handleSignal(int signal, siginfo_t*, void* context)
switch (signal) {
case VisitSignal: {
system->threadVisitor->visit(ip, stack, link);
globalSystem->threadVisitor->visit(ip, stack, link);
System::Thread* t = system->visitTarget;
system->visitTarget = 0;
System::Thread* t = globalSystem->visitTarget;
globalSystem->visitTarget = 0;
ACQUIRE_MONITOR(t, system->visitLock);
system->visitLock->notifyAll(t);
ACQUIRE_MONITOR(t, globalSystem->visitLock);
globalSystem->visitLock->notifyAll(t);
} break;
case InterruptSignal:
@ -987,9 +994,9 @@ void handleSignal(int signal, siginfo_t*, void* context)
namespace vm {
AVIAN_EXPORT System* makeSystem()
AVIAN_EXPORT System* makeSystem(bool reentrant)
{
return new (malloc(sizeof(MySystem))) MySystem();
return new (malloc(sizeof(MySystem))) MySystem(reentrant);
}
} // namespace vm

View File

@ -115,7 +115,7 @@ class MutexResource {
};
class MySystem;
MySystem* system;
MySystem* globalSystem;
DWORD WINAPI run(void* r)
{
@ -655,10 +655,12 @@ class MySystem : public System {
System::Library* next_;
};
MySystem()
MySystem(bool reentrant): reentrant(reentrant)
{
expect(this, system == 0);
system = this;
if (not reentrant) {
expect(this, globalSystem == 0);
globalSystem = this;
}
mutex = CreateMutex(0, false, 0);
assertT(this, mutex);
@ -1007,21 +1009,25 @@ class MySystem : public System {
virtual void dispose()
{
system = 0;
if (not reentrant) {
globalSystem = 0;
}
CloseHandle(mutex);
::free(this);
}
HANDLE mutex;
bool reentrant;
};
} // namespace
namespace vm {
AVIAN_EXPORT System* makeSystem()
AVIAN_EXPORT System* makeSystem(bool reentrant)
{
return new (malloc(sizeof(MySystem))) MySystem();
return new (malloc(sizeof(MySystem))) MySystem(reentrant);
}
} // namespace vm

View File

@ -306,69 +306,16 @@ unsigned targetFieldOffset(Thread* t, GcHashMap* typeMaps, GcField* field)
return offset;
}
GcTriple* makeCodeImage(Thread* t,
Zone* zone,
BootImage* image,
uint8_t* code,
const char* className,
const char* methodName,
const char* methodSpec,
void addClass(Thread* t,
GcClass* c,
const uint8_t* start,
size_t length,
GcHashMap* typeMaps)
{
PROTECT(t, c);
PROTECT(t, typeMaps);
t->m->classpath->interceptMethods(t);
GcTriple* constants = 0;
PROTECT(t, constants);
GcTriple* calls = 0;
PROTECT(t, calls);
GcPair* methods = 0;
PROTECT(t, methods);
DelayedPromise* addresses = 0;
class MyOffsetResolver : public OffsetResolver {
public:
MyOffsetResolver(GcHashMap** typeMaps) : typeMaps(typeMaps)
{
}
virtual unsigned fieldOffset(Thread* t, GcField* field)
{
return targetFieldOffset(t, *typeMaps, field);
}
GcHashMap** typeMaps;
} resolver(&typeMaps);
Finder* finder = static_cast<Finder*>(
roots(t)->bootLoader()->as<GcSystemClassLoader>(t)->finder());
for (Finder::Iterator it(finder); it.hasMore();) {
size_t nameSize = 0;
const char* name = it.next(&nameSize);
if (endsWith(".class", name, nameSize)
and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) {
if (false) {
fprintf(stderr, "pass 1 %.*s\n", (int)nameSize - 6, name);
}
GcClass* c
= resolveSystemClass(t,
roots(t)->bootLoader(),
makeByteArray(t, "%.*s", nameSize - 6, name),
true);
PROTECT(t, c);
System::Region* region = finder->find(name);
{
THREAD_RESOURCE(t, System::Region*, region, region->dispose());
class Client : public Stream::Client {
public:
Client(Thread* t) : t(t)
@ -384,7 +331,7 @@ GcTriple* makeCodeImage(Thread* t,
Thread* t;
} client(t);
Stream s(&client, region->start(), region->length());
Stream s(&client, start, length);
uint32_t magic = s.read4();
expect(t, magic == 0xCAFEBABE);
@ -437,8 +384,6 @@ GcTriple* makeCodeImage(Thread* t,
s.skip(s.read2());
break;
case CONSTANT_MethodHandle:
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
s.skip(3);
@ -469,8 +414,7 @@ GcTriple* makeCodeImage(Thread* t,
for (unsigned i = 0; i < count + 2; ++i) {
expect(t, i < map->buildFixedSizeInWords);
map->targetFixedOffsets()[i * BytesPerWord] = i
* TargetBytesPerWord;
map->targetFixedOffsets()[i * BytesPerWord] = i * TargetBytesPerWord;
init(new (map->fixedFields() + i) Field,
RUNTIME_ARRAY_BODY(types)[i],
@ -638,11 +582,9 @@ GcTriple* makeCodeImage(Thread* t,
}
}
if (hashMapFind(t,
typeMaps,
reinterpret_cast<object>(c),
objectHash,
objectEqual) == 0) {
if (hashMapFind(
t, typeMaps, reinterpret_cast<object>(c), objectHash, objectEqual)
== 0) {
GcByteArray* array = makeByteArray(
t,
TypeMap::sizeInBytes(ceilingDivide(c->fixedSize(), BytesPerWord),
@ -656,8 +598,7 @@ GcTriple* makeCodeImage(Thread* t,
for (unsigned i = 0; i < memberIndex; ++i) {
Field* f = RUNTIME_ARRAY_BODY(memberFields) + i;
expect(t,
f->buildOffset < map->buildFixedSizeInWords * BytesPerWord);
expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord);
map->targetFixedOffsets()[f->buildOffset] = f->targetOffset;
@ -686,8 +627,7 @@ GcTriple* makeCodeImage(Thread* t,
for (unsigned i = 0; i < staticIndex; ++i) {
Field* f = RUNTIME_ARRAY_BODY(staticFields) + i;
expect(t,
f->buildOffset < map->buildFixedSizeInWords * BytesPerWord);
expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord);
map->targetFixedOffsets()[f->buildOffset] = f->targetOffset;
@ -701,54 +641,43 @@ GcTriple* makeCodeImage(Thread* t,
objectHash);
}
}
}
}
}
for (Finder::Iterator it(finder); it.hasMore();) {
size_t nameSize = 0;
const char* name = it.next(&nameSize);
if (endsWith(".class", name, nameSize)
and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) {
if (false) {
fprintf(stderr, "pass 2 %.*s\n", (int)nameSize - 6, name);
}
GcClass* c = 0;
void compileMethods(Thread* t,
GcClass* c,
Zone* zone,
GcTriple** constants,
GcTriple** calls,
GcPair** methods,
DelayedPromise** addresses,
OffsetResolver* resolver,
JavaVM* hostVM,
const char* methodName,
const char* methodSpec)
{
PROTECT(t, c);
c = resolveSystemClass(t,
roots(t)->bootLoader(),
makeByteArray(t, "%.*s", nameSize - 6, name),
true);
if (GcArray* mtable = cast<GcArray>(t, c->methodTable())) {
PROTECT(t, mtable);
for (unsigned i = 0; i < mtable->length(); ++i) {
GcMethod* method = cast<GcMethod>(t, mtable->body()[i]);
if (((methodName == 0
or ::strcmp(
reinterpret_cast<char*>(method->name()->body().begin()),
or ::strcmp(reinterpret_cast<char*>(method->name()->body().begin()),
methodName) == 0)
and (methodSpec == 0
or ::strcmp(reinterpret_cast<char*>(
method->spec()->body().begin()),
or ::strcmp(
reinterpret_cast<char*>(method->spec()->body().begin()),
methodSpec) == 0))) {
if (method->code() or (method->flags() & ACC_NATIVE)) {
PROTECT(t, method);
t->m->processor->compileMethod(
t,
zone,
reinterpret_cast<GcTriple**>(&constants),
reinterpret_cast<GcTriple**>(&calls),
&addresses,
method,
&resolver);
t, zone, constants, calls, addresses, method, resolver, hostVM);
if (method->code()) {
methods = makePair(t,
*methods = makePair(t,
reinterpret_cast<object>(method),
reinterpret_cast<object>(methods));
reinterpret_cast<object>(*methods));
}
}
@ -767,10 +696,8 @@ GcTriple* makeCodeImage(Thread* t,
object o = singletonObject(t, addendum->pool(), index);
if (objectClass(t, o) == type(t, GcReference::Type)) {
o = reinterpret_cast<object>(
resolveClass(t,
roots(t)->bootLoader(),
cast<GcReference>(t, o)->name()));
o = reinterpret_cast<object>(resolveClass(
t, roots(t)->bootLoader(), cast<GcReference>(t, o)->name()));
addendum->pool()->setBodyElement(
t, index, reinterpret_cast<uintptr_t>(o));
@ -780,6 +707,134 @@ GcTriple* makeCodeImage(Thread* t,
}
}
}
}
GcTriple* makeCodeImage(Thread* t,
Zone* zone,
BootImage* image,
uint8_t* code,
JavaVM* hostVM,
const char* className,
const char* methodName,
const char* methodSpec,
GcHashMap* typeMaps)
{
PROTECT(t, typeMaps);
t->m->classpath->interceptMethods(t);
GcPair* classes = 0;
PROTECT(t, classes);
class MyOffsetResolver : public OffsetResolver {
public:
MyOffsetResolver(GcHashMap** typeMaps, GcPair** classes)
: typeMaps(typeMaps), classes(classes)
{
}
virtual unsigned fieldOffset(Thread* t, GcField* field)
{
return targetFieldOffset(t, *typeMaps, field);
}
virtual void addClass(Thread* t,
GcClass* c,
const uint8_t* start,
size_t length)
{
PROTECT(t, c);
*classes = makePair(
t, reinterpret_cast<object>(c), reinterpret_cast<object>(*classes));
return ::addClass(t, c, start, length, *typeMaps);
}
GcHashMap** typeMaps;
GcPair** classes;
} resolver(&typeMaps, &classes);
Finder* finder = static_cast<Finder*>(
roots(t)->bootLoader()->as<GcSystemClassLoader>(t)->finder());
for (Finder::Iterator it(finder); it.hasMore();) {
size_t nameSize = 0;
const char* name = it.next(&nameSize);
if (endsWith(".class", name, nameSize)
and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) {
if (false) {
fprintf(stderr, "pass 1 %.*s\n", (int)nameSize - 6, name);
}
GcClass* c
= resolveSystemClass(t,
roots(t)->bootLoader(),
makeByteArray(t, "%.*s", nameSize - 6, name),
true);
System::Region* region = finder->find(name);
THREAD_RESOURCE(t, System::Region*, region, region->dispose());
addClass(t, c, region->start(), region->length(), typeMaps);
}
}
GcTriple* constants = 0;
PROTECT(t, constants);
GcTriple* calls = 0;
PROTECT(t, calls);
GcPair* methods = 0;
PROTECT(t, methods);
DelayedPromise* addresses = 0;
for (Finder::Iterator it(finder); it.hasMore();) {
size_t nameSize = 0;
const char* name = it.next(&nameSize);
if (endsWith(".class", name, nameSize)
and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) {
if (false) {
fprintf(stderr, "pass 2 %.*s\n", (int)nameSize - 6, name);
}
GcClass* c
= resolveSystemClass(t,
roots(t)->bootLoader(),
makeByteArray(t, "%.*s", nameSize - 6, name),
true);
classes = makePair(
t, reinterpret_cast<object>(c), reinterpret_cast<object>(classes));
}
}
// Each method compilation may result in the creation of new,
// synthetic classes (e.g. for lambda expressions), so we must
// iterate until we've visited them all:
while (classes) {
GcPair* myClasses = classes;
PROTECT(t, myClasses);
classes = 0;
for (; myClasses; myClasses = cast<GcPair>(t, myClasses->second())) {
compileMethods(t,
cast<GcClass>(t, myClasses->first()),
zone,
&constants,
&calls,
&methods,
&addresses,
&resolver,
hostVM,
methodName,
methodSpec);
}
}
@ -1436,6 +1491,7 @@ void writeBootImage2(Thread* t,
OutputStream* codeOutput,
BootImage* image,
uint8_t* code,
JavaVM* hostVM,
const char* className,
const char* methodName,
const char* methodSpec,
@ -1656,8 +1712,15 @@ void writeBootImage2(Thread* t,
objectHash);
}
constants = makeCodeImage(
t, &zone, image, code, className, methodName, methodSpec, typeMaps);
constants = makeCodeImage(t,
&zone,
image,
code,
hostVM,
className,
methodName,
methodSpec,
typeMaps);
PROTECT(t, constants);
@ -1927,21 +1990,23 @@ uint64_t writeBootImage(Thread* t, uintptr_t* arguments)
OutputStream* codeOutput = reinterpret_cast<OutputStream*>(arguments[1]);
BootImage* image = reinterpret_cast<BootImage*>(arguments[2]);
uint8_t* code = reinterpret_cast<uint8_t*>(arguments[3]);
const char* className = reinterpret_cast<const char*>(arguments[4]);
const char* methodName = reinterpret_cast<const char*>(arguments[5]);
const char* methodSpec = reinterpret_cast<const char*>(arguments[6]);
JavaVM* hostVM = reinterpret_cast<JavaVM*>(arguments[4]);
const char* className = reinterpret_cast<const char*>(arguments[5]);
const char* methodName = reinterpret_cast<const char*>(arguments[6]);
const char* methodSpec = reinterpret_cast<const char*>(arguments[7]);
const char* bootimageStart = reinterpret_cast<const char*>(arguments[7]);
const char* bootimageEnd = reinterpret_cast<const char*>(arguments[8]);
const char* codeimageStart = reinterpret_cast<const char*>(arguments[9]);
const char* codeimageEnd = reinterpret_cast<const char*>(arguments[10]);
bool useLZMA = arguments[11];
const char* bootimageStart = reinterpret_cast<const char*>(arguments[8]);
const char* bootimageEnd = reinterpret_cast<const char*>(arguments[9]);
const char* codeimageStart = reinterpret_cast<const char*>(arguments[10]);
const char* codeimageEnd = reinterpret_cast<const char*>(arguments[11]);
bool useLZMA = arguments[12];
writeBootImage2(t,
bootimageOutput,
codeOutput,
image,
code,
hostVM,
className,
methodName,
methodSpec,
@ -1969,6 +2034,8 @@ class Arguments {
const char* bootimage;
const char* codeimage;
const char* hostvm;
char* entryClass;
char* entryMethod;
char* entrySpec;
@ -2008,6 +2075,7 @@ class Arguments {
Arg classpath(parser, true, "cp", "<classpath>");
Arg bootimage(parser, true, "bootimage", "<bootimage file>");
Arg codeimage(parser, true, "codeimage", "<codeimage file>");
Arg hostvm(parser, false, "hostvm", "<host vm>");
Arg entry(
parser, false, "entry", "<class name>[.<method name>[<method spec>]]");
Arg bootimageSymbols(parser,
@ -2028,6 +2096,7 @@ class Arguments {
this->classpath = classpath.value;
this->bootimage = bootimage.value;
this->codeimage = codeimage.value;
this->hostvm = hostvm.value;
this->useLZMA = useLZMA.value != 0;
if (entry.value) {
@ -2100,6 +2169,7 @@ class Arguments {
"classpath = %s\n"
"bootimage = %s\n"
"codeimage = %s\n"
"hostvm = %s\n"
"entryClass = %s\n"
"entryMethod = %s\n"
"entrySpec = %s\n"
@ -2110,6 +2180,7 @@ class Arguments {
classpath,
bootimage,
codeimage,
hostvm,
entryClass,
entryMethod,
entrySpec,
@ -2168,10 +2239,66 @@ int main(int ac, const char** av)
return -1;
}
JavaVM* hostVM = 0;
System::Library* hostVMLibrary = 0;
if (args.hostvm) {
if (s->success(s->load(&hostVMLibrary, args.hostvm))) {
typedef jint(JNICALL * CreateVM)(Machine**, Thread**, void*);
const char* name = "JNI_CreateJavaVM";
CreateVM createVM
= reinterpret_cast<CreateVM>(hostVMLibrary->resolve(name));
if (createVM) {
JavaVMInitArgs vmArgs;
vmArgs.version = JNI_VERSION_1_6;
vmArgs.nOptions = 2;
vmArgs.ignoreUnrecognized = JNI_TRUE;
#define CLASSPATH_PROPERTY "-Xbootclasspath:"
const char* classpath = args.classpath;
size_t classpathSize = strlen(classpath);
size_t classpathPropertyBufferSize = sizeof(CLASSPATH_PROPERTY)
+ classpathSize;
RUNTIME_ARRAY(
char, classpathPropertyBuffer, classpathPropertyBufferSize);
memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer),
CLASSPATH_PROPERTY,
sizeof(CLASSPATH_PROPERTY) - 1);
memcpy(RUNTIME_ARRAY_BODY(classpathPropertyBuffer)
+ sizeof(CLASSPATH_PROPERTY) - 1,
classpath,
classpathSize + 1);
JavaVMOption options[2];
options[0].optionString = RUNTIME_ARRAY_BODY(classpathPropertyBuffer);
options[1].optionString = const_cast<char*>("-Davian.reentrant=true");
vmArgs.options = options;
Thread* dummy;
if (JNI_OK != createVM(&hostVM, &dummy, &vmArgs)) {
fprintf(stderr, "unable to initialize host VM\n");
hostVMLibrary->disposeAll();
return -1;
}
} else {
fprintf(stderr, "unable to find %s in %s\n", name, args.hostvm);
hostVMLibrary->disposeAll();
return -1;
}
} else {
fprintf(stderr, "unable to open %s\n", args.hostvm);
return -1;
}
}
uintptr_t arguments[] = {reinterpret_cast<uintptr_t>(&bootimageOutput),
reinterpret_cast<uintptr_t>(&codeOutput),
reinterpret_cast<uintptr_t>(&image),
reinterpret_cast<uintptr_t>(code.begin()),
reinterpret_cast<uintptr_t>(hostVM),
reinterpret_cast<uintptr_t>(args.entryClass),
reinterpret_cast<uintptr_t>(args.entryMethod),
reinterpret_cast<uintptr_t>(args.entrySpec),
@ -2183,6 +2310,11 @@ int main(int ac, const char** av)
run(t, writeBootImage, arguments);
if (hostVM) {
hostVM->vtable->DestroyJavaVM(hostVM);
hostVMLibrary->disposeAll();
}
if (t->exception) {
printTrace(t, t->exception);
return -1;