diff --git a/classpath/avian/Callback.java b/classpath/avian/Callback.java new file mode 100644 index 0000000000..25889f3b2c --- /dev/null +++ b/classpath/avian/Callback.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface Callback { + public void handleResult(T result); + public void handleException(Throwable exception); +} diff --git a/classpath/avian/CallbackReceiver.java b/classpath/avian/CallbackReceiver.java new file mode 100644 index 0000000000..c36b1416bd --- /dev/null +++ b/classpath/avian/CallbackReceiver.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public interface CallbackReceiver { + public T receive(Callback callback) throws Exception; +} diff --git a/classpath/java/util/Cell.java b/classpath/avian/Cell.java similarity index 92% rename from classpath/java/util/Cell.java rename to classpath/avian/Cell.java index 4903a80a8b..fe902feebe 100644 --- a/classpath/java/util/Cell.java +++ b/classpath/avian/Cell.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package java.util; +package avian; public class Cell { public T value; diff --git a/classpath/avian/Continuations.java b/classpath/avian/Continuations.java new file mode 100644 index 0000000000..5bc1deeefd --- /dev/null +++ b/classpath/avian/Continuations.java @@ -0,0 +1,238 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +import java.util.concurrent.Callable; + +/** + * This class provides methods to capture continuations and manage + * control flow when calling continuations. + * + *

A continuation is a snapshot of a thread's call stack which can + * be captured via callWithCurrentContinuation and later + * restored any number of times. The program may restore this + * snapshot by either feeding it a result (to be returned by + * callWithCurrentContinuation) or feeding it an + * exception (to be thrown by + * callWithCurrentContinuation). Continuations may be + * used to implement features such as coroutines, generators, and + * cooperative multitasking. + * + *

This class provides two static methods, + * callWithCurrentContinuation and + * dynamicWind, with similar semantics to the Scheme + * functions call-with-current-continuation and + * dynamic-wind, respectively. In addition, we define + * how continuations work with respect to native code, exceptions, + * try/finally blocks, synchronized blocks, and multithreading. + * + *

Continuations and Continuation Contexts

+ * + *

A continuation can be thought of as a singly-linked list of + * stack frames representing the call trace, where the head of the + * list is the frame of the method most recently called (i.e. the top + * of the stack). However, this trace only extends as far as the most + * recent chain of Java frames - it ends just prior to the most recent + * native frame in the stack. The reason for this is that the VM + * cannot, in general, safely capture and restore native frames. + * Therefore, each call from native code to Java (including the + * original invocation of main(String[]) or + * Thread.run()) represents a new continuation context in + * which continuations may be captured, and these will only contain + * frames from within that context. + * + *

Calling a continuation (i.e. feeding it a result or exception) + * causes the current continuation to be replaced with the calling + * continuation. When the last method in this new continuation + * returns, it returns to the native frame which created the current + * context, which may not be the same as the context in which that + * continuation was created. + * + *

We define the return type of a continuation context as the + * return type of the first method called in that context. A + * continuation may be called from a different context than the one in + * which it was created, provided the return type of the latter is + * compatible with the current context. + * + *

Given a thread executing in context "A" which wants to call a + * continuation created in context "B", the following rules apply: + * + *

    + * + *
  • If the return type of "A" is void, the return + * type of "B" may be anything, including void
  • + * + *
  • If the return type of "A" is a primitive type, the return + * type of "B" must match exactly
  • + * + *
  • If the return type of "A" is an object type, that type must + * assignable from the return type of "B" (i.e. the latter must + * either be the same as the former or a superclass or + * superinterface of it)
  • + * + *
+ * + *

A thread may call a continuation created by a different thread + * provided the return types are compatible. Multiple threads may + * safely call the same continuation simultaneously without + * synchronization. Any attempt to call a continuation from a context + * with an incompatible return type will throw an {@link + * avian.IncompatibleContinuationException}. + * + *

Winding, Unwinding, and Rewinding

+ * + *

Traditionally, Java provides one way to wind the execution stack + * (recursive method calls) and two ways to unwind it (normal returns + * and exception unwinding). With continuations, we add a new way to + * rewind the stack and a new way to unwind it. + * + *

The call stack of a continuation may share frames with other + * continuations - in which case they share a common history. When + * calling a continuation "B" from the current continuation "A", the + * VM must unwind past any frames which are in "A" but not in "B" and + * rewind past any frames in "B" but not in "A". During this + * unwinding and rewinding, control may pass through synchronized and + * try/finally blocks while going down the old stack and up the new + * stack. + * + *

However, unlike the traditional processes of winding and + * unwinding, the VM will ignore these blocks - monitors will not be + * released or acquired and finally blocks will not execute. This is + * by design. The purpose of such a block is to acquire a resource, + * such as a file handle or monitor, once before executing a task and + * release it after the task is finished, regardless of how often the + * task might temporarily yield control to other continuations. + * + *

Conversely, one might wish to acquire and release a resource + * each time control (re)winds to or unwinds from a continuation, + * respectively. In this case, we use dynamicWind to + * register functions which will run every time that frame is passed, + * regardless of how the stack is wound or unwound. + */ +public class Continuations { + private Continuations() { } + + /** + * Captures the current continuation, passing a reference to the + * specified receiver. + * + *

This method will either return the result returned by + * receiver.receive(Callback), propagate the exception + * thrown by that method, return the result passed to the + * handleResult(T) method of the continuation, or throw the + * exception passed to the handleException(Throwable) of the + * continuation. + */ + public static native T callWithCurrentContinuation + (CallbackReceiver receiver) throws Exception; + + /** + * Calls the specified "before" and "after" tasks each time a + * continuation containing the call is wound or unwound, + * respectively. + * + *

This method first calls before.run(), then + * thunk.call(), and finally after.run(), + * returning the result of the second call. If + * before.run() does not return normally, the second + * and third calls will not happen. If thunk.call() + * throws an exception, after.run(), will be called + * before the exception is propagated. + * + *

If thunk.call() calls a continuation (directly or + * via a subroutine) which does not include the current call to + * dynamicWind, after.run() will be called + * before control passes to that continuation. If this call throws + * an exception, the exception will propagate to the current caller + * of dynamicWind. + * + *

If thunk.call() creates a continuation which is + * later called from a continuation which does not include the + * current call to dynamicWind, + * before.run() will be called before control passes to + * that continuation. As above, if this call throws an exception, + * the exception will propagate to the current caller of + * dynamicWind. + */ + public static T dynamicWind(Runnable before, + Callable thunk, + Runnable after) + throws Exception + { + UnwindResult result = dynamicWind2(before, thunk, after); + if (result.continuation != null) { + after.run(); + if (result.exception != null) { + result.continuation.handleException(result.exception); + } else { + result.continuation.handleResult(result.result); + } + throw new AssertionError(); + } else { + return (T) result.result; + } + } + + private static native UnwindResult dynamicWind2(Runnable before, + Callable thunk, + Runnable after) + throws Exception; + + private static UnwindResult wind(Runnable before, + Callable thunk, + Runnable after) + throws Exception + { + before.run(); + + try { + return new UnwindResult(null, thunk.call(), null); + } finally { + after.run(); + } + } + + private static void rewind(Runnable before, + Callback continuation, + Object result, + Throwable exception) + throws Exception + { + before.run(); + + if (exception != null) { + continuation.handleException(exception); + } else { + continuation.handleResult(result); + } + + throw new AssertionError(); + } + + private static class Continuation implements Callback { + public native void handleResult(T result); + public native void handleException(Throwable exception); + } + + private static class UnwindResult { + public final Callback continuation; + public final Object result; + public final Throwable exception; + + public UnwindResult(Callback continuation, Object result, + Throwable exception) + { + this.continuation = continuation; + this.result = result; + this.exception = exception; + } + } +} diff --git a/classpath/avian/IncompatibleContinuationException.java b/classpath/avian/IncompatibleContinuationException.java new file mode 100644 index 0000000000..49641c0f27 --- /dev/null +++ b/classpath/avian/IncompatibleContinuationException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public class IncompatibleContinuationException extends Exception { + public IncompatibleContinuationException(String message) { + super(message); + } + + public IncompatibleContinuationException() { + super(); + } +} diff --git a/classpath/avian/Machine.java b/classpath/avian/Machine.java new file mode 100644 index 0000000000..cbc62efae6 --- /dev/null +++ b/classpath/avian/Machine.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian; + +public abstract class Machine { + + public static native void dumpHeap(String outputFile); + +} diff --git a/classpath/java/util/PersistentSet.java b/classpath/avian/PersistentSet.java similarity index 99% rename from classpath/java/util/PersistentSet.java rename to classpath/avian/PersistentSet.java index 3cbe4431f4..0441b38238 100644 --- a/classpath/java/util/PersistentSet.java +++ b/classpath/avian/PersistentSet.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -8,7 +8,9 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package java.util; +package avian; + +import java.util.Comparator; public class PersistentSet implements Iterable { private static final Node NullNode = new Node(null); diff --git a/classpath/java/lang/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java similarity index 97% rename from classpath/java/lang/SystemClassLoader.java rename to classpath/avian/SystemClassLoader.java index 723651ca10..e85d240a85 100644 --- a/classpath/java/lang/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package java.lang; +package avian; import java.net.URL; import java.net.MalformedURLException; diff --git a/classpath/avian/resource/Handler.java b/classpath/avian/resource/Handler.java new file mode 100644 index 0000000000..a10e575a85 --- /dev/null +++ b/classpath/avian/resource/Handler.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package avian.resource; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class Handler extends URLStreamHandler { + protected URLConnection openConnection(URL url) { + return new ResourceConnection(url); + } + + private static class ResourceConnection extends URLConnection { + public ResourceConnection(URL url) { + super(url); + } + + public int getContentLength() { + return ResourceInputStream.getContentLength(url.getFile()); + } + + public InputStream getInputStream() throws IOException { + return new ResourceInputStream(url.getFile()); + } + } + + private static class ResourceInputStream extends InputStream { + private long peer; + private int position; + + public ResourceInputStream(String path) throws IOException { + peer = open(path); + if (peer == 0) { + throw new FileNotFoundException(path); + } + } + + private static native int getContentLength(String path); + + private static native long open(String path) throws IOException; + + private static native int read(long peer, int position) throws IOException; + + private static native int read(long peer, int position, + byte[] b, int offset, int length) + throws IOException; + + public static native void close(long peer) throws IOException; + + public int read() throws IOException { + if (peer != 0) { + int c = read(peer, position); + if (c >= 0) { + ++ position; + } + return c; + } else { + throw new IOException(); + } + } + + public int read(byte[] b, int offset, int length) throws IOException { + if (peer != 0) { + if (b == null) { + throw new NullPointerException(); + } + + if (offset < 0 || offset + length > b.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + int c = read(peer, position, b, offset, length); + if (c >= 0) { + position += c; + } + return c; + } else { + throw new IOException(); + } + } + + public void close() throws IOException { + if (peer != 0) { + close(peer); + peer = 0; + } + } + } +} diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 1bf1a04fec..9fbf2087bb 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -305,6 +305,21 @@ Java_java_io_File_isDirectory(JNIEnv* e, jclass, jstring path) } } +extern "C" JNIEXPORT jboolean JNICALL +Java_java_io_File_isFile(JNIEnv* e, jclass, jstring path) +{ + const char* chars = e->GetStringUTFChars(path, 0); + if (chars) { + STRUCT_STAT s; + int r = STAT(chars, &s); + bool v = (r == 0 and S_ISREG(s.st_mode)); + e->ReleaseStringUTFChars(path, chars); + return v; + } else { + return false; + } +} + extern "C" JNIEXPORT jboolean JNICALL Java_java_io_File_exists(JNIEnv* e, jclass, jstring path) { diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index f0b651bfd9..1225ca9e69 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -7,7 +7,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ - + #include "math.h" #include "stdlib.h" #include "sys/time.h" @@ -32,11 +32,14 @@ # define SO_PREFIX "" #else # define SO_PREFIX "lib" +#include +#include "sys/utsname.h" #include "sys/wait.h" #endif #ifdef __APPLE__ # define SO_SUFFIX ".jnilib" +#include #elif defined WIN32 # define SO_SUFFIX ".dll" #else @@ -349,6 +352,44 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, r = e->NewStringUTF("\\"); } else if (strcmp(chars, "os.name") == 0) { r = e->NewStringUTF("Windows"); + } else if (strcmp(chars, "os.version") == 0) { + unsigned size = 32; + char buffer[size]; + OSVERSIONINFO OSversion; + OSversion.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); + ::GetVersionEx(&OSversion); + snprintf(buffer, size, "%i.%i", (int)OSversion.dwMajorVersion, (int)OSversion.dwMinorVersion); + r = e->NewStringUTF(buffer); + } else if (strcmp(chars, "os.arch") == 0) { + #ifdef __i386__ + r = e->NewStringUTF("x86"); + #else + #ifdef __x86_64__ + r = e->NewStringUTF("x86_64"); + #else + #if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) + r = e->NewStringUTF("ppc"); + #else + #ifdef __ia64__ + r = e->NewStringUTF("ia64"); + #else + #ifdef __arm__ + r = e->NewStringUTF("arm"); + #else + #ifdef __alpha__ + r = e->NewStringUTF("alpha"); + #else + #ifdef __sparc64__ + r = e->NewStringUTF("sparc64"); + #else + r = e->NewStringUTF("unknown"); + #endif + #endif + #endif + #endif + #endif + #endif + #endif } else if (strcmp(chars, "java.io.tmpdir") == 0) { TCHAR buffer[MAX_PATH]; GetTempPath(MAX_PATH, buffer); @@ -368,10 +409,56 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, #else r = e->NewStringUTF("Linux"); #endif + } else if (strcmp(chars, "os.version") == 0) { +#ifdef __APPLE__ + unsigned size = 32; + char buffer[size]; + long minorVersion, majorVersion; + + Gestalt(gestaltSystemVersionMajor, &majorVersion); + Gestalt(gestaltSystemVersionMinor, &minorVersion); + + snprintf(buffer, size, "%ld.%ld", majorVersion, minorVersion); + r = e->NewStringUTF(buffer); +#else + struct utsname system_id; + uname(&system_id); + r = e->NewStringUTF(system_id.release); +#endif + } else if (strcmp(chars, "os.arch") == 0) { + #ifdef __i386__ + r = e->NewStringUTF("x86"); + #else + #ifdef __x86_64__ + r = e->NewStringUTF("x86_64"); + #else + #if defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__) + r = e->NewStringUTF("ppc"); + #else + #ifdef __ia64__ + r = e->NewStringUTF("ia64"); + #else + #ifdef __arm__ + r = e->NewStringUTF("arm"); + #else + #ifdef __alpha__ + r = e->NewStringUTF("alpha"); + #else + #ifdef __sparc64__ + r = e->NewStringUTF("sparc64"); + #else + r = e->NewStringUTF("unknown"); + #endif + #endif + #endif + #endif + #endif + #endif + #endif } else if (strcmp(chars, "java.io.tmpdir") == 0) { r = e->NewStringUTF("/tmp"); } else if (strcmp(chars, "user.home") == 0) { - r = e->NewStringUTF(getenv("HOME")); + r = e->NewStringUTF(getenv("HOME")); } #endif diff --git a/classpath/java-nio.cpp b/classpath/java-nio.cpp index e5a0e345ff..899982a472 100644 --- a/classpath/java-nio.cpp +++ b/classpath/java-nio.cpp @@ -154,17 +154,19 @@ eagain() } bool -makeNonblocking(JNIEnv* e, int d) +setBlocking(JNIEnv* e, int d, bool blocking) { #ifdef WIN32 - u_long a = 1; + u_long a = (blocking ? 0 : 1); int r = ioctlsocket(d, FIONBIO, &a); if (r != 0) { throwIOException(e); return false; } #else - int r = fcntl(d, F_SETFL, fcntl(d, F_GETFL) | O_NONBLOCK); + int r = fcntl(d, F_SETFL, (blocking + ? (fcntl(d, F_GETFL) & (~O_NONBLOCK)) + : (fcntl(d, F_GETFL) | O_NONBLOCK))); if (r < 0) { throwIOException(e); return false; @@ -231,7 +233,6 @@ doAccept(JNIEnv* e, int s) socklen_t length = sizeof(address); int r = ::accept(s, &address, &length); if (r >= 0) { - makeNonblocking(e, r); return r; } else { throwIOException(e); @@ -260,7 +261,7 @@ doWrite(int fd, const void* buffer, size_t count) } int -makeSocket(JNIEnv* e, bool blocking = false) +makeSocket(JNIEnv* e) { #ifdef WIN32 static bool wsaInitialized = false; @@ -279,8 +280,6 @@ makeSocket(JNIEnv* e, bool blocking = false) return s; } - if (not blocking) makeNonblocking(e, s); - return s; } @@ -311,6 +310,15 @@ Java_java_nio_channels_ServerSocketChannel_natDoListen(JNIEnv *e, return s; } +extern "C" JNIEXPORT void JNICALL +Java_java_nio_channels_SocketChannel_configureBlocking(JNIEnv *e, + jclass, + jint socket, + jboolean blocking) +{ + setBlocking(e, socket, blocking); +} + extern "C" JNIEXPORT void JNICALL Java_java_nio_channels_SocketChannel_natSetTcpNoDelay(JNIEnv *e, jclass, @@ -325,11 +333,14 @@ Java_java_nio_channels_SocketChannel_natDoConnect(JNIEnv *e, jclass, jstring host, jint port, + jboolean blocking, jbooleanArray retVal) { int s = makeSocket(e); if (e->ExceptionOccurred()) return 0; + setBlocking(e, s, blocking); + sockaddr_in address; init(e, &address, host, port); if (e->ExceptionOccurred()) return 0; @@ -425,7 +436,8 @@ class Pipe { address.sin_family = AF_INET; address.sin_port = 0; address.sin_addr.s_addr = inet_addr("127.0.0.1"); //INADDR_LOOPBACK; - listener_ = makeSocket(e, false); + listener_ = makeSocket(e); + setBlocking(e, listener_, false); ::doListen(e, listener_, &address); socklen_t length = sizeof(sockaddr_in); @@ -435,7 +447,8 @@ class Pipe { throwIOException(e); } - writer_ = makeSocket(e, true); + writer_ = makeSocket(e); + setBlocking(e, writer_, true); connected_ = ::doConnect(e, writer_, &address); } @@ -485,8 +498,8 @@ class Pipe { return; } - if (makeNonblocking(e, pipe[0])) { - makeNonblocking(e, pipe[1]); + if (setBlocking(e, pipe[0], false)) { + setBlocking(e, pipe[1], false); } } @@ -626,7 +639,17 @@ Java_java_nio_channels_SocketSelector_natDoSocketSelect(JNIEnv *e, jclass, } #endif - timeval time = { interval / 1000, (interval % 1000) * 1000 }; + timeval time; + if (interval > 0) { + time.tv_sec = interval / 1000; + time.tv_usec = (interval % 1000) * 1000; + } else if (interval < 0) { + time.tv_sec = 0; + time.tv_usec = 0; + } else { + time.tv_sec = INT32_MAX; + time.tv_usec = 0; + } int r = ::select(max + 1, &(s->read), &(s->write), &(s->except), &time); if (r < 0) { diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 7308eb4872..48c8078aed 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -41,6 +41,12 @@ public class File { return isDirectory(path); } + private static native boolean isFile(String path); + + public boolean isFile() { + return isFile(path); + } + public String getName() { int index = path.lastIndexOf(FileSeparator); if (index >= 0) { @@ -141,6 +147,19 @@ public class File { return mkdir(); } + public File[] listFiles() { + return listFiles(null); + } + + public File[] listFiles(FilenameFilter filter) { + String[] list = list(filter); + File[] result = new File[list.length]; + for (int i = 0; i < list.length; ++i) { + result[i] = new File(this, list[i]); + } + return result; + } + public String[] list() { return list(null); } diff --git a/classpath/java/lang/Appendable.java b/classpath/java/lang/Appendable.java new file mode 100644 index 0000000000..4c3b10bf22 --- /dev/null +++ b/classpath/java/lang/Appendable.java @@ -0,0 +1,22 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +import java.io.IOException; + +public interface Appendable { + public Appendable append(char c) throws IOException; + + public Appendable append(CharSequence sequence) throws IOException; + + public Appendable append(CharSequence sequence, int start, int end) + throws IOException; +} diff --git a/classpath/java/lang/ArrayIndexOutOfBoundsException.java b/classpath/java/lang/ArrayIndexOutOfBoundsException.java index a2e8e4c634..7545661bb7 100644 --- a/classpath/java/lang/ArrayIndexOutOfBoundsException.java +++ b/classpath/java/lang/ArrayIndexOutOfBoundsException.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,7 +10,7 @@ package java.lang; -public class ArrayIndexOutOfBoundsException extends RuntimeException { +public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { public ArrayIndexOutOfBoundsException(String message, Throwable cause) { super(message, cause); } diff --git a/classpath/java/lang/Character.java b/classpath/java/lang/Character.java index 7d097366a8..61c3b3bedb 100644 --- a/classpath/java/lang/Character.java +++ b/classpath/java/lang/Character.java @@ -187,4 +187,40 @@ public final class Character implements Comparable { return isHighSurrogate(high) && isLowSurrogate(low); } + public static int codePointAt(CharSequence sequence, int offset) { + int length = sequence.length(); + if (offset < 0 || offset >= length) { + throw new IndexOutOfBoundsException(); + } + + char high = sequence.charAt(offset); + if (! isHighSurrogate(high) || offset >= length) { + return high; + } + char low = sequence.charAt(offset + 1); + if (! isLowSurrogate(low)) { + return high; + } + + return toCodePoint(high, low); + } + + public static int codePointCount(CharSequence sequence, int start, int end) { + int length = sequence.length(); + if (start < 0 || start > end || end >= length) { + throw new IndexOutOfBoundsException(); + } + + int count = 0; + for (int i = start; i < end; ++i) { + if (isHighSurrogate(sequence.charAt(i)) + && (i + 1) < end + && isLowSurrogate(sequence.charAt(i + 1))) + { + ++ i; + } + ++ count; + } + return count; + } } diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index 1a18b60fe8..f439ba3521 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -14,13 +14,20 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.GenericDeclaration; +import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.IOException; import java.net.URL; +import java.security.ProtectionDomain; +import java.security.Permissions; +import java.security.AllPermission; -public final class Class { - private static final int PrimitiveFlag = 1 << 4; +public final class Class implements Type, GenericDeclaration { + private static final int PrimitiveFlag = 1 << 5; private short flags; private byte vmFlags; @@ -55,6 +62,34 @@ public final class Class { } public String getName() { + if (name == null) { + if ((vmFlags & PrimitiveFlag) != 0) { + if (this == primitiveClass('V')) { + name = "void".getBytes(); + } else if (this == primitiveClass('Z')) { + name = "boolean".getBytes(); + } else if (this == primitiveClass('B')) { + name = "byte".getBytes(); + } else if (this == primitiveClass('C')) { + name = "char".getBytes(); + } else if (this == primitiveClass('S')) { + name = "short".getBytes(); + } else if (this == primitiveClass('I')) { + name = "int".getBytes(); + } else if (this == primitiveClass('F')) { + name = "float".getBytes(); + } else if (this == primitiveClass('J')) { + name = "long".getBytes(); + } else if (this == primitiveClass('D')) { + name = "double".getBytes(); + } else { + throw new AssertionError(); + } + } else { + throw new AssertionError(); + } + } + return new String (replace('/', '.', name, 0, name.length - 1), 0, name.length - 1, false); } @@ -84,6 +119,9 @@ public final class Class { ClassLoader loader) throws ClassNotFoundException { + if (loader == null) { + loader = Class.class.loader; + } Class c = loader.loadClass(name); if (initialize) { c.initialize(); @@ -436,8 +474,63 @@ public final class Class { return null; } } - + public boolean desiredAssertionStatus() { return false; } + + public T cast(Object o) { + return (T) o; + } + + public Object[] getSigners() { + throw new UnsupportedOperationException(); + } + + public Annotation[] getDeclaredAnnotations() { + throw new UnsupportedOperationException(); + } + + public boolean isEnum() { + throw new UnsupportedOperationException(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } + + public String getSimpleName() { + throw new UnsupportedOperationException(); + } + + public Method getEnclosingMethod() { + throw new UnsupportedOperationException(); + } + + public Constructor getEnclosingConstructor() { + throw new UnsupportedOperationException(); + } + + public Class getEnclosingClass() { + throw new UnsupportedOperationException(); + } + + public Class[] getDeclaredClasses() { + throw new UnsupportedOperationException(); + } + + public A getAnnotation(Class c) { + throw new UnsupportedOperationException(); + } + + public ProtectionDomain getProtectionDomain() { + Permissions p = new Permissions(); + p.add(new AllPermission()); + return new ProtectionDomain(null, p); + } + + // for GNU Classpath compatibility: + void setSigners(Object[] signers) { + throw new UnsupportedOperationException(); + } } diff --git a/classpath/java/lang/ClassNotFoundException.java b/classpath/java/lang/ClassNotFoundException.java index e2752a538a..1dfc694b06 100644 --- a/classpath/java/lang/ClassNotFoundException.java +++ b/classpath/java/lang/ClassNotFoundException.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,8 +11,11 @@ package java.lang; public class ClassNotFoundException extends Exception { + private final Throwable cause2; + public ClassNotFoundException(String message, Throwable cause) { super(message, cause); + cause2 = cause; } public ClassNotFoundException(String message) { diff --git a/classpath/java/lang/Enum.java b/classpath/java/lang/Enum.java index 5edcbf6cd5..db1c7fa496 100644 --- a/classpath/java/lang/Enum.java +++ b/classpath/java/lang/Enum.java @@ -53,4 +53,12 @@ public abstract class Enum> implements Comparable { public String toString() { return name; } + + public Class getDeclaringClass() { + Class c = getClass(); + while (c.getSuperclass() != Enum.class) { + c = c.getSuperclass(); + } + return c; + } } diff --git a/classpath/java/lang/ExceptionInInitializerError.java b/classpath/java/lang/ExceptionInInitializerError.java index 912e43ba8d..9deea39355 100644 --- a/classpath/java/lang/ExceptionInInitializerError.java +++ b/classpath/java/lang/ExceptionInInitializerError.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,11 +11,14 @@ package java.lang; public class ExceptionInInitializerError extends Error { + private final Throwable cause2; + public ExceptionInInitializerError(String message) { super(message); + cause2 = null; } public ExceptionInInitializerError() { - super(); + this(null); } } diff --git a/classpath/java/lang/Long.java b/classpath/java/lang/Long.java index 272c4365a9..279a8220ed 100644 --- a/classpath/java/lang/Long.java +++ b/classpath/java/lang/Long.java @@ -66,7 +66,7 @@ public final class Long extends Number implements Comparable { char[] array = new char[size]; - int i = array.length - 1; + int i = size - 1; for (long n = v; n != 0; n /= radix) { long digit = n % radix; if (negative) digit = -digit; @@ -83,7 +83,7 @@ public final class Long extends Number implements Comparable { array[i] = '-'; } - return new String(array); + return new String(array, 0, size, false); } public static String toString(long v) { diff --git a/classpath/java/lang/Object.java b/classpath/java/lang/Object.java index b902b38060..a1fb1a0521 100644 --- a/classpath/java/lang/Object.java +++ b/classpath/java/lang/Object.java @@ -25,7 +25,7 @@ public class Object { return this == o; } - protected void finalize() { } + protected void finalize() throws Throwable { } public native final Class getClass(); @@ -41,5 +41,14 @@ public class Object { wait(0); } - public native final void wait(long timeout) throws InterruptedException; + public native final void wait(long milliseconds) throws InterruptedException; + + public final void wait(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds != 0) { + ++ milliseconds; + } + wait(milliseconds); + } } diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index 63a029301a..51f15006c7 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -75,8 +75,6 @@ public class Runtime { public native long totalMemory(); - public static native void dumpHeap(String outputFile); - private static class MyProcess extends Process { private long pid; private final int in; diff --git a/classpath/java/lang/StackOverflowError.java b/classpath/java/lang/StackOverflowError.java index c5acc3fb6a..d8144b6666 100644 --- a/classpath/java/lang/StackOverflowError.java +++ b/classpath/java/lang/StackOverflowError.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,9 +10,9 @@ package java.lang; -public class StackOverflowError extends Error { +public class StackOverflowError extends VirtualMachineError { public StackOverflowError(String message) { - super(message, null); + super(message); } public StackOverflowError() { diff --git a/classpath/java/lang/StackTraceElement.java b/classpath/java/lang/StackTraceElement.java index f20dc27803..cd63e42292 100644 --- a/classpath/java/lang/StackTraceElement.java +++ b/classpath/java/lang/StackTraceElement.java @@ -18,8 +18,8 @@ public class StackTraceElement { private String file; private int line; - private StackTraceElement(String class_, String method, String file, - int line) + public StackTraceElement(String class_, String method, String file, + int line) { this.class_ = class_; this.method = method; @@ -56,7 +56,7 @@ public class StackTraceElement { } public String getClassName() { - return class_; + return class_.replace('/', '.'); } public String getMethodName() { diff --git a/classpath/java/lang/String.java b/classpath/java/lang/String.java index e7a6aabfff..79dcc8ee75 100644 --- a/classpath/java/lang/String.java +++ b/classpath/java/lang/String.java @@ -12,14 +12,30 @@ package java.lang; import java.io.UnsupportedEncodingException; import java.util.regex.Pattern; +import java.util.Comparator; +import java.util.Locale; import java.io.ByteArrayOutputStream; +import java.io.Serializable; + +public final class String + implements Comparable, CharSequence, Serializable +{ + public static Comparator CASE_INSENSITIVE_ORDER + = new Comparator() { + public int compare(String a, String b) { + return a.compareToIgnoreCase(b); + } + }; -public final class String implements Comparable, CharSequence { private final Object data; private final int offset; private final int length; private int hashCode; + public String() { + this(new char[0], 0, 0); + } + public String(char[] data, int offset, int length, boolean copy) { this((Object) data, offset, length, copy); } @@ -32,9 +48,11 @@ public final class String implements Comparable, CharSequence { this(data, 0, data.length); } - public String(byte bytes[], int offset, int length, String charsetName) throws UnsupportedEncodingException { + public String(byte bytes[], int offset, int length, String charsetName) + throws UnsupportedEncodingException + { this(bytes, offset, length); - if (!charsetName.equals("UTF-8")) { + if (! charsetName.equalsIgnoreCase("UTF-8")) { throw new UnsupportedEncodingException(charsetName); } } @@ -57,12 +75,29 @@ public final class String implements Comparable, CharSequence { public String(byte[] data, String charset) throws UnsupportedEncodingException - { - this(data); - if (! charset.equals("UTF-8")) { - throw new UnsupportedEncodingException(charset); - } + { + this(data); + if (! charset.equals("UTF-8")) { + throw new UnsupportedEncodingException(charset); } + } + + public String(byte bytes[], int highByte, int offset, int length) { + if (offset < 0 || offset + length > bytes.length) { + throw new IndexOutOfBoundsException + (offset + " < 0 or " + offset + " + " + length + " > " + bytes.length); + } + + char[] c = new char[length]; + int mask = highByte << 8; + for (int i = 0; i < length; ++i) { + c[i] = (char) ((bytes[offset + i] & 0xFF) | mask); + } + + this.data = c; + this.offset = 0; + this.length = length; + } private String(Object data, int offset, int length, boolean copy) { int l; @@ -200,7 +235,7 @@ public final class String implements Comparable, CharSequence { } public boolean equalsIgnoreCase(String s) { - return this == s || compareToIgnoreCase(s) == 0; + return this == s || (s != null && compareToIgnoreCase(s) == 0); } public int compareTo(String s) { @@ -381,7 +416,7 @@ public final class String implements Comparable, CharSequence { } } else { throw new IndexOutOfBoundsException - (start + " not in (0, " + end + ") or " + end + " > " + length); + (start + " not in [0, " + end + ") or " + end + " > " + length); } } @@ -534,6 +569,10 @@ public final class String implements Comparable, CharSequence { return Pattern.matches(regex, this); } + public String replaceAll(String regex, String replacement) { + return Pattern.compile(regex).matcher(this).replaceAll(replacement); + } + public native String intern(); public static String valueOf(Object s) { @@ -557,7 +596,9 @@ public final class String implements Comparable, CharSequence { } public static String valueOf(int v) { - return Integer.toString(v); + // use Integer.toString(int, int), because GNU Classpath's + // Integer.toString(int) just calls String.valueOf(int): + return Integer.toString(v, 10); } public static String valueOf(long v) { @@ -572,6 +613,14 @@ public final class String implements Comparable, CharSequence { return Double.toString(v); } + public static String valueOf(char[] data, int offset, int length) { + return new String(data, offset, length); + } + + public static String valueOf(char[] data) { + return valueOf(data, 0, data.length); + } + public int lastIndexOf(int ch, int lastIndex) { for (int i = lastIndex ; i >= 0; --i) { if (charAt(i) == ch) { @@ -581,4 +630,63 @@ public final class String implements Comparable, CharSequence { return -1; } + + public boolean regionMatches(int thisOffset, String match, int matchOffset, + int length) + { + return regionMatches(false, thisOffset, match, matchOffset, length); + } + + public boolean regionMatches(boolean ignoreCase, int thisOffset, + String match, int matchOffset, int length) + { + String a = substring(thisOffset, thisOffset + length); + String b = match.substring(matchOffset, matchOffset + length); + if (ignoreCase) { + return a.equalsIgnoreCase(b); + } else { + return a.equals(b); + } + } + + public boolean isEmpty() { + return length == 0; + } + + public boolean contains(String match) { + return indexOf(match) != -1; + } + + public int codePointAt(int offset) { + return Character.codePointAt(this, offset); + } + + public int codePointCount(int start, int end) { + return Character.codePointCount(this, start, end); + } + + public String replace(CharSequence match, CharSequence replacement) { + throw new UnsupportedOperationException(); + } + + public String toUpperCase(Locale locale) { + throw new UnsupportedOperationException(); + } + + public String toLowerCase(Locale locale) { + throw new UnsupportedOperationException(); + } + + // for GNU Classpath compatibility: + static char[] zeroBasedStringValue(String s) { + if (s.offset == 0) { + if (s.data instanceof char[]) { + char[] data = (char[]) s.data; + if (data.length == s.length) { + return data; + } + } + } + return s.toCharArray(); + } } diff --git a/classpath/java/lang/StringBuilder.java b/classpath/java/lang/StringBuilder.java index 47b58134d1..77332204a5 100644 --- a/classpath/java/lang/StringBuilder.java +++ b/classpath/java/lang/StringBuilder.java @@ -10,7 +10,7 @@ package java.lang; -public class StringBuilder implements CharSequence { +public class StringBuilder implements CharSequence, Appendable { private static final int BufferSize = 32; private Cell chain; @@ -54,6 +54,14 @@ public class StringBuilder implements CharSequence { } } + public StringBuilder append(CharSequence sequence) { + return append(sequence.toString()); + } + + public Appendable append(CharSequence sequence, int start, int end) { + return append(sequence.subSequence(start, end)); + } + public StringBuilder append(char[] b, int offset, int length) { return append(new String(b, offset, length)); } @@ -150,6 +158,10 @@ public class StringBuilder implements CharSequence { return this; } + public StringBuilder insert(int i, CharSequence s) { + return insert(i, s.toString()); + } + public StringBuilder insert(int i, char c) { return insert(i, new String(new char[] { c }, 0, 1, false)); } @@ -332,4 +344,8 @@ public class StringBuilder implements CharSequence { deleteCharAt(index); insert(index, ch); } + + public void ensureCapacity(int capacity) { + // ignore + } } diff --git a/classpath/java/lang/Thread.java b/classpath/java/lang/Thread.java index 731331051d..4279e5bfbf 100644 --- a/classpath/java/lang/Thread.java +++ b/classpath/java/lang/Thread.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -15,14 +15,30 @@ import java.util.WeakHashMap; public class Thread implements Runnable { private long peer; + private boolean interrupted; + private boolean daemon; + private byte state; + private byte priority; private final Runnable task; private Map locals; - private boolean interrupted; private Object sleepLock; private ClassLoader classLoader; - private String name; + private UncaughtExceptionHandler exceptionHandler; - public Thread(Runnable task, String name) { + // package private for GNU Classpath, which inexplicably bypasses + // the accessor methods: + String name; + ThreadGroup group; + + private static UncaughtExceptionHandler defaultExceptionHandler; + + public static final int MIN_PRIORITY = 1; + public static final int NORM_PRIORITY = 5; + public static final int MAX_PRIORITY = 10; + + public Thread(ThreadGroup group, Runnable task, String name, long stackSize) + { + this.group = group; this.task = task; this.name = name; @@ -41,8 +57,28 @@ public class Thread implements Runnable { classLoader = current.classLoader; } + public Thread(ThreadGroup group, Runnable task, String name) { + this(group, task, name, 0); + } + + public Thread(ThreadGroup group, String name) { + this(null, null, name); + } + + public Thread(Runnable task, String name) { + this(null, task, name); + } + public Thread(Runnable task) { - this(task, "Thread["+task+"]"); + this(null, task, "Thread["+task+"]"); + } + + public Thread(String name) { + this(null, null, name); + } + + public Thread() { + this((Runnable) null); } public synchronized void start() { @@ -58,6 +94,28 @@ public class Thread implements Runnable { private native long doStart(); + private static void run(Thread t) throws Throwable { + t.state = (byte) State.RUNNABLE.ordinal(); + try { + t.run(); + } catch (Throwable e) { + UncaughtExceptionHandler eh = t.exceptionHandler; + UncaughtExceptionHandler deh = defaultExceptionHandler; + if (eh != null) { + eh.uncaughtException(t, e); + } else if (deh != null) { + deh.uncaughtException(t, e); + } else { + throw e; + } + } finally { + synchronized (t) { + t.state = (byte) State.TERMINATED.ordinal(); + t.notifyAll(); + } + } + } + public void run() { if (task != null) { task.run(); @@ -101,6 +159,10 @@ public class Thread implements Runnable { } } + public static boolean isInterrupted() { + return currentThread().interrupted; + } + public static void sleep(long milliseconds) throws InterruptedException { Thread t = currentThread(); if (t.sleepLock == null) { @@ -111,6 +173,16 @@ public class Thread implements Runnable { } } + public static void sleep(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + sleep(milliseconds); + } + public StackTraceElement[] getStackTrace() { return Throwable.resolveTrace(getStackTrace(peer)); } @@ -124,5 +196,125 @@ public class Thread implements Runnable { public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + UncaughtExceptionHandler eh = exceptionHandler; + return (eh == null ? group : eh); + } + + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultExceptionHandler; + } + + public void setUncaughtExceptionHandler(UncaughtExceptionHandler h) { + exceptionHandler = h; + } + + public static void setDefaultUncaughtExceptionHandler + (UncaughtExceptionHandler h) + { + defaultExceptionHandler = h; + } + + public State getState() { + return State.values()[state]; + } + + public boolean isAlive() { + switch (getState()) { + case NEW: + case TERMINATED: + return false; + + default: + return true; + } + } + + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + if (priority < MIN_PRIORITY || priority > MAX_PRIORITY) { + throw new IllegalArgumentException(); + } + this.priority = (byte) priority; + } + + public boolean isDaemon() { + return daemon; + } + + public void setDaemon(boolean v) { + daemon = v; + } + + public static native void yield(); + + public synchronized void join() throws InterruptedException { + while (getState() != State.TERMINATED) { + wait(); + } + } + + public synchronized void join(long milliseconds) throws InterruptedException + { + long then = System.currentTimeMillis(); + long remaining = milliseconds; + while (remaining > 0 && getState() != State.TERMINATED) { + wait(remaining); + + remaining = milliseconds - (System.currentTimeMillis() - then); + } + } + + public void join(long milliseconds, int nanoseconds) + throws InterruptedException + { + if (nanoseconds > 0) { + ++ milliseconds; + } + + join(milliseconds); + } + + public ThreadGroup getThreadGroup() { + return group; + } + + public static native boolean holdsLock(Object o); + + public long getId() { + return peer; + } + + public void stop() { + throw new UnsupportedOperationException(); + } + + public void stop(Throwable t) { + throw new UnsupportedOperationException(); + } + + public void suspend() { + throw new UnsupportedOperationException(); + } + + public void resume() { + throw new UnsupportedOperationException(); + } + + public interface UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e); + } + + public enum State { + NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED + } } diff --git a/classpath/java/lang/ThreadDeath.java b/classpath/java/lang/ThreadDeath.java new file mode 100644 index 0000000000..167f6633dc --- /dev/null +++ b/classpath/java/lang/ThreadDeath.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ThreadDeath extends Error { + public ThreadDeath() { } +} diff --git a/classpath/java/lang/ThreadGroup.java b/classpath/java/lang/ThreadGroup.java new file mode 100644 index 0000000000..9636b6a53a --- /dev/null +++ b/classpath/java/lang/ThreadGroup.java @@ -0,0 +1,35 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class ThreadGroup implements Thread.UncaughtExceptionHandler { + private final ThreadGroup parent; + private final String name; + + public ThreadGroup(ThreadGroup parent, String name) { + this.parent = parent; + this.name = name; + } + + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else { + Thread.UncaughtExceptionHandler deh + = Thread.getDefaultUncaughtExceptionHandler(); + if (deh != null) { + deh.uncaughtException(t, e); + } else if (! (e instanceof ThreadDeath)) { + e.printStackTrace(); + } + } + } +} diff --git a/classpath/java/lang/Throwable.java b/classpath/java/lang/Throwable.java index 333a156166..09861fe9b4 100644 --- a/classpath/java/lang/Throwable.java +++ b/classpath/java/lang/Throwable.java @@ -13,8 +13,9 @@ package java.lang; import java.io.PrintStream; import java.io.PrintWriter; import java.io.IOException; +import java.io.Serializable; -public class Throwable { +public class Throwable implements Serializable { private String message; private Object trace; private Throwable cause; @@ -109,4 +110,9 @@ public class Throwable { cause.printStackTrace(sb, nl); } } + + public Throwable fillInStackTrace() { + trace = trace(0); + return this; + } } diff --git a/classpath/java/lang/VirtualMachineError.java b/classpath/java/lang/VirtualMachineError.java new file mode 100644 index 0000000000..b59ebd41be --- /dev/null +++ b/classpath/java/lang/VirtualMachineError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class VirtualMachineError extends Error { + public VirtualMachineError(String message) { + super(message); + } + + public VirtualMachineError() { + this(null); + } +} diff --git a/classpath/java/lang/ref/Reference.java b/classpath/java/lang/ref/Reference.java index cdfacb191a..ed322a8c42 100644 --- a/classpath/java/lang/ref/Reference.java +++ b/classpath/java/lang/ref/Reference.java @@ -22,6 +22,10 @@ public abstract class Reference { this.queue = queue; } + protected Reference(T target) { + this(target, null); + } + public T get() { return target; } diff --git a/classpath/java/lang/reflect/Constructor.java b/classpath/java/lang/reflect/Constructor.java index b4254e7c82..11df2f1fae 100644 --- a/classpath/java/lang/reflect/Constructor.java +++ b/classpath/java/lang/reflect/Constructor.java @@ -10,7 +10,9 @@ package java.lang.reflect; -public class Constructor extends AccessibleObject implements Member { +public class Constructor extends AccessibleObject + implements Member, GenericDeclaration +{ private Method method; public Constructor(Method method) { @@ -46,6 +48,18 @@ public class Constructor extends AccessibleObject implements Member { return method.getName(); } + public boolean isSynthetic() { + return method.isSynthetic(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } + + public Type[] getGenericParameterTypes() { + return method.getGenericParameterTypes(); + } + private static native T make(Class c); public T newInstance(Object ... arguments) diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 978859ce05..19df851027 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -101,6 +101,38 @@ public class Field extends AccessibleObject { } } + public boolean getBoolean(Object instance) throws IllegalAccessException { + return ((Boolean) get(instance)).booleanValue(); + } + + public byte getByte(Object instance) throws IllegalAccessException { + return ((Byte) get(instance)).byteValue(); + } + + public short getShort(Object instance) throws IllegalAccessException { + return ((Short) get(instance)).shortValue(); + } + + public char getChar(Object instance) throws IllegalAccessException { + return ((Character) get(instance)).charValue(); + } + + public int getInt(Object instance) throws IllegalAccessException { + return ((Integer) get(instance)).intValue(); + } + + public float getFloat(Object instance) throws IllegalAccessException { + return ((Float) get(instance)).floatValue(); + } + + public long getLong(Object instance) throws IllegalAccessException { + return ((Long) get(instance)).longValue(); + } + + public double getDouble(Object instance) throws IllegalAccessException { + return ((Double) get(instance)).doubleValue(); + } + public void set(Object instance, Object value) throws IllegalAccessException { @@ -162,6 +194,10 @@ public class Field extends AccessibleObject { } } + public boolean isEnumConstant() { + throw new UnsupportedOperationException(); + } + private static native long getPrimitive (Object instance, int code, int offset); diff --git a/classpath/java/lang/reflect/GenericDeclaration.java b/classpath/java/lang/reflect/GenericDeclaration.java new file mode 100644 index 0000000000..61914661bb --- /dev/null +++ b/classpath/java/lang/reflect/GenericDeclaration.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface GenericDeclaration { + public TypeVariable[] getTypeParameters(); +} diff --git a/classpath/java/lang/reflect/Member.java b/classpath/java/lang/reflect/Member.java index 4654d38875..6af945750f 100644 --- a/classpath/java/lang/reflect/Member.java +++ b/classpath/java/lang/reflect/Member.java @@ -19,4 +19,6 @@ public interface Member { public int getModifiers(); public String getName(); + + public boolean isSynthetic(); } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 68058aedce..f6ac1bd6cd 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -10,7 +10,9 @@ package java.lang.reflect; -public class Method extends AccessibleObject implements Member { +public class Method extends AccessibleObject + implements Member, GenericDeclaration +{ private byte vmFlags; private byte returnCode; private byte parameterCount; @@ -124,4 +126,28 @@ public class Method extends AccessibleObject implements Member { } throw new RuntimeException(); } + + public boolean isSynthetic() { + throw new UnsupportedOperationException(); + } + + public Object getDefaultValue() { + throw new UnsupportedOperationException(); + } + + public Type[] getGenericParameterTypes() { + throw new UnsupportedOperationException(); + } + + public Type getGenericReturnType() { + throw new UnsupportedOperationException(); + } + + public Class[] getExceptionTypes() { + throw new UnsupportedOperationException(); + } + + public TypeVariable>[] getTypeParameters() { + throw new UnsupportedOperationException(); + } } diff --git a/classpath/java/lang/reflect/Modifier.java b/classpath/java/lang/reflect/Modifier.java index a2671ad4d3..042554f298 100644 --- a/classpath/java/lang/reflect/Modifier.java +++ b/classpath/java/lang/reflect/Modifier.java @@ -35,4 +35,5 @@ public final class Modifier { public static boolean isSuper (int v) { return (v & SUPER) != 0; } public static boolean isNative (int v) { return (v & NATIVE) != 0; } public static boolean isAbstract (int v) { return (v & ABSTRACT) != 0; } + public static boolean isInterface(int v) { return (v & INTERFACE) != 0; } } diff --git a/classpath/java/lang/reflect/Type.java b/classpath/java/lang/reflect/Type.java new file mode 100644 index 0000000000..b1a04ec765 --- /dev/null +++ b/classpath/java/lang/reflect/Type.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface Type { } diff --git a/classpath/java/lang/reflect/TypeVariable.java b/classpath/java/lang/reflect/TypeVariable.java new file mode 100644 index 0000000000..65d531d6bf --- /dev/null +++ b/classpath/java/lang/reflect/TypeVariable.java @@ -0,0 +1,19 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang.reflect; + +public interface TypeVariable + extends Type +{ + public Type[] getBounds(); + public T getGenericDeclaration(); + public String getName(); +} diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index bf2174c447..2b405b307e 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -11,7 +11,6 @@ package java.net; import java.io.IOException; -import java.io.FileNotFoundException; import java.io.InputStream; public final class URL { @@ -73,7 +72,7 @@ public final class URL { throws MalformedURLException { if ("resource".equals(protocol)) { - return new ResourceHandler(); + return new avian.resource.Handler(); } else { throw new MalformedURLException("unknown protocol: " + protocol); } @@ -88,87 +87,4 @@ public final class URL { this.file = file; this.ref = ref; } - - private static class ResourceHandler extends URLStreamHandler { - protected URLConnection openConnection(URL url) { - return new ResourceConnection(url); - } - } - - private static class ResourceConnection extends URLConnection { - public ResourceConnection(URL url) { - super(url); - } - - public int getContentLength() { - return ResourceInputStream.getContentLength(url.getFile()); - } - - public InputStream getInputStream() throws IOException { - return new ResourceInputStream(url.getFile()); - } - } - - private static class ResourceInputStream extends InputStream { - private long peer; - private int position; - - public ResourceInputStream(String path) throws IOException { - peer = open(path); - if (peer == 0) { - throw new FileNotFoundException(path); - } - } - - private static native int getContentLength(String path); - - private static native long open(String path) throws IOException; - - private static native int read(long peer, int position) throws IOException; - - private static native int read(long peer, int position, - byte[] b, int offset, int length) - throws IOException; - - public static native void close(long peer) throws IOException; - - public int read() throws IOException { - if (peer != 0) { - int c = read(peer, position); - if (c >= 0) { - ++ position; - } - return c; - } else { - throw new IOException(); - } - } - - public int read(byte[] b, int offset, int length) throws IOException { - if (peer != 0) { - if (b == null) { - throw new NullPointerException(); - } - - if (offset < 0 || offset + length > b.length) { - throw new ArrayIndexOutOfBoundsException(); - } - - int c = read(peer, position, b, offset, length); - if (c >= 0) { - position += c; - } - return c; - } else { - throw new IOException(); - } - } - - public void close() throws IOException { - if (peer != 0) { - close(peer); - peer = 0; - } - } - } } diff --git a/classpath/java/nio/channels/Selector.java b/classpath/java/nio/channels/Selector.java index 021f267f17..7716db60e9 100644 --- a/classpath/java/nio/channels/Selector.java +++ b/classpath/java/nio/channels/Selector.java @@ -42,7 +42,11 @@ public abstract class Selector { public abstract Selector wakeup(); + public abstract int selectNow() throws IOException; + public abstract int select(long interval) throws IOException; + public abstract int select() throws IOException; + public abstract void close(); } diff --git a/classpath/java/nio/channels/ServerSocketChannel.java b/classpath/java/nio/channels/ServerSocketChannel.java index f84c9626bc..5ddbdc0f16 100644 --- a/classpath/java/nio/channels/ServerSocketChannel.java +++ b/classpath/java/nio/channels/ServerSocketChannel.java @@ -23,7 +23,7 @@ public class ServerSocketChannel extends SelectableChannel { return new ServerSocketChannel(); } - public SelectableChannel configureBlocking(boolean v) { + public SelectableChannel configureBlocking(boolean v) throws IOException { return channel.configureBlocking(v); } @@ -61,6 +61,7 @@ public class ServerSocketChannel extends SelectableChannel { throw new IllegalArgumentException(); } channel.socket = doListen(a.getHostName(), a.getPort()); + channel.configureBlocking(channel.isBlocking()); } } diff --git a/classpath/java/nio/channels/SocketChannel.java b/classpath/java/nio/channels/SocketChannel.java index 33ac011005..c6879d3a8a 100644 --- a/classpath/java/nio/channels/SocketChannel.java +++ b/classpath/java/nio/channels/SocketChannel.java @@ -24,21 +24,29 @@ public class SocketChannel extends SelectableChannel int socket = InvalidSocket; boolean connected = false; + boolean blocking = true; public static SocketChannel open() { return new SocketChannel(); } - public SelectableChannel configureBlocking(boolean v) { - if (v) throw new IllegalArgumentException(); + public SelectableChannel configureBlocking(boolean v) throws IOException { + blocking = v; + if (socket != InvalidSocket) { + configureBlocking(socket, v); + } return this; } + public boolean isBlocking() { + return blocking; + } + public Socket socket() { return new Handle(); } - public boolean connect(SocketAddress address) throws Exception { + public boolean connect(SocketAddress address) throws IOException { InetSocketAddress a; try { a = (InetSocketAddress) address; @@ -46,6 +54,7 @@ public class SocketChannel extends SelectableChannel throw new UnsupportedAddressTypeException(); } socket = doConnect(a.getHostName(), a.getPort()); + configureBlocking(blocking); return connected; } @@ -56,11 +65,11 @@ public class SocketChannel extends SelectableChannel } } - private int doConnect(String host, int port) throws Exception { + private int doConnect(String host, int port) throws IOException { if (host == null) throw new NullPointerException(); boolean b[] = new boolean[1]; - int s = natDoConnect(host, port, b); + int s = natDoConnect(host, port, blocking, b); connected = b[0]; return s; } @@ -109,11 +118,14 @@ public class SocketChannel extends SelectableChannel } } + private static native void configureBlocking(int socket, boolean blocking) + throws IOException; + private static native void natSetTcpNoDelay(int socket, boolean on) throws SocketException; - private static native int natDoConnect(String host, int port, boolean[] connected) - throws Exception; + private static native int natDoConnect(String host, int port, boolean blocking, boolean[] connected) + throws IOException; private static native int natRead(int socket, byte[] buffer, int offset, int length) throws IOException; private static native int natWrite(int socket, byte[] buffer, int offset, int length) diff --git a/classpath/java/nio/channels/SocketSelector.java b/classpath/java/nio/channels/SocketSelector.java index 587340a508..79d01d8594 100644 --- a/classpath/java/nio/channels/SocketSelector.java +++ b/classpath/java/nio/channels/SocketSelector.java @@ -48,7 +48,21 @@ class SocketSelector extends Selector { } } + public synchronized int selectNow() throws IOException { + return doSelect(-1); + } + + public synchronized int select() throws IOException { + return doSelect(0); + } + public synchronized int select(long interval) throws IOException { + if (interval < 0) throw new IllegalArgumentException(); + + return doSelect(interval); + } + + public int doSelect(long interval) throws IOException { selectedKeys.clear(); if (clearWoken()) return 0; diff --git a/classpath/java/security/AllPermission.java b/classpath/java/security/AllPermission.java new file mode 100644 index 0000000000..6bc99d53e8 --- /dev/null +++ b/classpath/java/security/AllPermission.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class AllPermission extends Permission { } diff --git a/classpath/java/security/CodeSource.java b/classpath/java/security/CodeSource.java new file mode 100644 index 0000000000..030597fa22 --- /dev/null +++ b/classpath/java/security/CodeSource.java @@ -0,0 +1,13 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class CodeSource { } diff --git a/classpath/java/security/Permission.java b/classpath/java/security/Permission.java new file mode 100644 index 0000000000..bc6c438e18 --- /dev/null +++ b/classpath/java/security/Permission.java @@ -0,0 +1,17 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public abstract class Permission { + public PermissionCollection newPermissionCollection() { + return null; + } +} diff --git a/classpath/java/security/PermissionCollection.java b/classpath/java/security/PermissionCollection.java new file mode 100644 index 0000000000..6e2de457e9 --- /dev/null +++ b/classpath/java/security/PermissionCollection.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public abstract class PermissionCollection { + public abstract void add(Permission p); +} diff --git a/classpath/java/security/Permissions.java b/classpath/java/security/Permissions.java new file mode 100644 index 0000000000..2c2b688972 --- /dev/null +++ b/classpath/java/security/Permissions.java @@ -0,0 +1,41 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; + +public class Permissions extends PermissionCollection { + private final Map collections = new HashMap(); + + public void add(Permission p) { + Class c = p.getClass(); + PermissionCollection pc = collections.get(c); + if (pc == null) { + pc = p.newPermissionCollection(); + if (pc == null) { + pc = new MyPermissionCollection(); + } + collections.put(c, pc); + } + pc.add(p); + } + + private static class MyPermissionCollection extends PermissionCollection { + private final Set permissions = new HashSet(); + + public void add(Permission p) { + permissions.add(p); + } + } +} diff --git a/classpath/java/security/ProtectionDomain.java b/classpath/java/security/ProtectionDomain.java new file mode 100644 index 0000000000..f7670e28a4 --- /dev/null +++ b/classpath/java/security/ProtectionDomain.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.security; + +public class ProtectionDomain { + private final CodeSource codeSource; + private final PermissionCollection permissions; + + public ProtectionDomain(CodeSource codeSource, + PermissionCollection permissions) + { + this.codeSource = codeSource; + this.permissions = permissions; + } +} diff --git a/classpath/java/util/AbstractCollection.java b/classpath/java/util/AbstractCollection.java index 5093b9a612..81500614ec 100644 --- a/classpath/java/util/AbstractCollection.java +++ b/classpath/java/util/AbstractCollection.java @@ -62,6 +62,10 @@ public abstract class AbstractCollection implements Collection { public abstract int size(); + public Object[] toArray() { + return toArray(new Object[size()]); + } + public S[] toArray(S[] array) { return Collections.toArray(this, array); } diff --git a/classpath/java/util/AbstractList.java b/classpath/java/util/AbstractList.java new file mode 100644 index 0000000000..fa1abbd65a --- /dev/null +++ b/classpath/java/util/AbstractList.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractList extends AbstractCollection + implements List +{ } diff --git a/classpath/java/util/AbstractSequentialList.java b/classpath/java/util/AbstractSequentialList.java new file mode 100644 index 0000000000..8af8938ba0 --- /dev/null +++ b/classpath/java/util/AbstractSequentialList.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public abstract class AbstractSequentialList extends AbstractList + implements List +{ } diff --git a/classpath/java/util/ArrayList.java b/classpath/java/util/ArrayList.java index d6ca7d07d3..faabdebb5c 100644 --- a/classpath/java/util/ArrayList.java +++ b/classpath/java/util/ArrayList.java @@ -10,7 +10,7 @@ package java.util; -public class ArrayList implements List { +public class ArrayList extends AbstractList { private static final int MinimumCapacity = 16; private Object[] array; @@ -94,7 +94,7 @@ public class ArrayList implements List { return true; } - public int indexOf(T element) { + public int indexOf(Object element) { for (int i = 0; i < size; ++i) { if (equal(element, array[i])) { return i; @@ -103,8 +103,8 @@ public class ArrayList implements List { return -1; } - public int lastIndexOf(T element) { - for (int i = size; i >= 0; --i) { + public int lastIndexOf(Object element) { + for (int i = size - 1; i >= 0; --i) { if (equal(element, array[i])) { return i; } @@ -159,23 +159,23 @@ public class ArrayList implements List { return size() == 0; } - public S[] toArray(S[] a) { - return Collections.toArray(this, a); - } - public void clear() { array = null; size = 0; } public Iterator iterator() { - return new Collections.ArrayListIterator(this); + return listIterator(); } public ListIterator listIterator(int index) { return new Collections.ArrayListIterator(this, index); } + public ListIterator listIterator() { + return listIterator(0); + } + public String toString() { return Collections.toString(this); } diff --git a/classpath/java/util/Arrays.java b/classpath/java/util/Arrays.java index db41e6bddc..2fe49fbc67 100644 --- a/classpath/java/util/Arrays.java +++ b/classpath/java/util/Arrays.java @@ -73,6 +73,24 @@ public class Arrays { return false; } + public int indexOf(Object element) { + for (int i = 0; i < array.length; ++i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + + public int lastIndexOf(Object element) { + for (int i = array.length - 1; i >= 0; --i) { + if (equal(element, array[i])) { + return i; + } + } + return -1; + } + public T get(int index) { return array[index]; } @@ -81,6 +99,10 @@ public class Arrays { throw new UnsupportedOperationException(); } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public S[] toArray(S[] a) { return (S[])array; } @@ -102,7 +124,15 @@ public class Arrays { } public Iterator iterator() { - return new Collections.ArrayListIterator(this); + return listIterator(); + } + + public ListIterator listIterator(int index) { + return new Collections.ArrayListIterator(this, index); + } + + public ListIterator listIterator() { + return listIterator(0); } }; } diff --git a/classpath/java/util/Collection.java b/classpath/java/util/Collection.java index aa8eee6d89..77fb9c8d3e 100644 --- a/classpath/java/util/Collection.java +++ b/classpath/java/util/Collection.java @@ -23,6 +23,8 @@ public interface Collection extends Iterable { public boolean remove(Object element); + public Object[] toArray(); + public S[] toArray(S[] array); public void clear(); diff --git a/classpath/java/util/Collections.java b/classpath/java/util/Collections.java index 619a4c5c61..00facfaa0e 100644 --- a/classpath/java/util/Collections.java +++ b/classpath/java/util/Collections.java @@ -64,6 +64,22 @@ public class Collections { return sb.toString(); } + static String toString(Map m) { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (Iterator it = m.entrySet().iterator(); it.hasNext();) { + Map.Entry e = it.next(); + sb.append(e.getKey()) + .append("=") + .append(e.getValue()); + if (it.hasNext()) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + static class IteratorEnumeration implements Enumeration { private final Iterator it; @@ -113,6 +129,10 @@ public class Collections { synchronized (lock) { return collection.remove((T)e); } } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public T[] toArray(T[] array) { synchronized (lock) { return collection.toArray(array); } } @@ -182,7 +202,6 @@ public class Collections { return new SynchronizedMap (map); } - static class SynchronizedSet extends SynchronizedCollection implements Set @@ -216,7 +235,7 @@ public class Collections { static class ArrayListIterator implements ListIterator { private final List list; - private boolean canRemove = false; + private int toRemove = -1; private int index; public ArrayListIterator(List list) { @@ -234,7 +253,7 @@ public class Collections { public T previous() { if (hasPrevious()) { - canRemove = true; + toRemove = index; return list.get(index--); } else { throw new NoSuchElementException(); @@ -243,8 +262,8 @@ public class Collections { public T next() { if (hasNext()) { - canRemove = true; - return list.get(++index); + toRemove = ++index; + return list.get(index); } else { throw new NoSuchElementException(); } @@ -255,9 +274,10 @@ public class Collections { } public void remove() { - if (canRemove) { - canRemove = false; - list.remove(index--); + if (toRemove != -1) { + list.remove(toRemove); + index = toRemove - 1; + toRemove = -1; } else { throw new IllegalStateException(); } @@ -303,13 +323,56 @@ public class Collections { return inner.size(); } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public S[] toArray(S[] array) { return inner.toArray(array); - } - + } } public static Set unmodifiableSet(Set hs) { return new UnmodifiableSet(hs); } + + static class KeyIterator implements Iterator { + private final Iterator> it; + + public KeyIterator(Iterator> it) { + this.it = it; + } + + public K next() { + return it.next().getKey(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } + + static class ValueIterator implements Iterator { + private final Iterator> it; + + public ValueIterator(Iterator> it) { + this.it = it; + } + + public V next() { + return it.next().getValue(); + } + + public boolean hasNext() { + return it.hasNext(); + } + + public void remove() { + it.remove(); + } + } } diff --git a/classpath/java/util/HashMap.java b/classpath/java/util/HashMap.java index dd6eb85f61..ecae9a01f1 100644 --- a/classpath/java/util/HashMap.java +++ b/classpath/java/util/HashMap.java @@ -40,19 +40,7 @@ public class HashMap implements Map { } public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - for (Iterator> it = iterator(); it.hasNext();) { - Entry e = it.next(); - sb.append(e.getKey()) - .append("=") - .append(e.getValue()); - if (it.hasNext()) { - sb.append(","); - } - } - sb.append("}"); - return sb.toString(); + return Collections.toString(this); } private static int nextPowerOfTwo(int n) { @@ -324,7 +312,8 @@ public class HashMap implements Map { } public boolean contains(Object o) { - return (o instanceof Entry) ? containsKey(((Entry)o).getKey()) : false; + return (o instanceof Entry) + && containsKey(((Entry)o).getKey()); } public boolean add(Entry e) { @@ -337,14 +326,18 @@ public class HashMap implements Map { return change; } - public boolean remove(Object o) { - return (o instanceof Entry) ? remove((Entry)o) : false; - } + public boolean remove(Object o) { + return (o instanceof Entry) && remove((Entry)o); + } public boolean remove(Entry e) { return removeCell(e.getKey()) != null; } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public T[] toArray(T[] array) { return Collections.toArray(this, array); } @@ -385,6 +378,10 @@ public class HashMap implements Map { return removeCell(key) != null; } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public T[] toArray(T[] array) { return Collections.toArray(this, array); } @@ -394,11 +391,10 @@ public class HashMap implements Map { } public Iterator iterator() { - return new KeyIterator(new MyIterator()); + return new Collections.KeyIterator(new MyIterator()); } } - private class Values implements Collection { public int size() { return HashMap.this.size(); @@ -424,6 +420,10 @@ public class HashMap implements Map { throw new UnsupportedOperationException(); } + public Object[] toArray() { + return toArray(new Object[size()]); + } + public T[] toArray(T[] array) { return Collections.toArray(this, array); } @@ -433,7 +433,7 @@ public class HashMap implements Map { } public Iterator iterator() { - return new ValueIterator(new MyIterator()); + return new Collections.ValueIterator(new MyIterator()); } } @@ -495,44 +495,4 @@ public class HashMap implements Map { } } } - - private static class KeyIterator implements Iterator { - private final Iterator> it; - - public KeyIterator(Iterator> it) { - this.it = it; - } - - public K next() { - return it.next().getKey(); - } - - public boolean hasNext() { - return it.hasNext(); - } - - public void remove() { - it.remove(); - } - } - - private static class ValueIterator implements Iterator { - private final Iterator> it; - - public ValueIterator(Iterator> it) { - this.it = it; - } - - public V next() { - return it.next().getValue(); - } - - public boolean hasNext() { - return it.hasNext(); - } - - public void remove() { - it.remove(); - } - } } diff --git a/classpath/java/util/HashSet.java b/classpath/java/util/HashSet.java index b89c317a35..aa86f47df5 100644 --- a/classpath/java/util/HashSet.java +++ b/classpath/java/util/HashSet.java @@ -10,7 +10,7 @@ package java.util; -public class HashSet implements Set { +public class HashSet extends AbstractSet implements Set { private static final Object Value = new Object(); private final HashMap map; @@ -54,10 +54,6 @@ public class HashSet implements Set { return map.remove(element) != Value; } - public T[] toArray(T[] array) { - return Collections.toArray(this, array); - } - public void clear() { map.clear(); } diff --git a/classpath/java/util/Hashtable.java b/classpath/java/util/Hashtable.java index 978847a6b7..9715a42a24 100644 --- a/classpath/java/util/Hashtable.java +++ b/classpath/java/util/Hashtable.java @@ -28,7 +28,7 @@ public class Hashtable implements Map { } } - public String toString() { + public synchronized String toString() { return map.toString(); } diff --git a/classpath/java/util/LinkedList.java b/classpath/java/util/LinkedList.java index 657d245997..55bc80fed9 100644 --- a/classpath/java/util/LinkedList.java +++ b/classpath/java/util/LinkedList.java @@ -10,7 +10,7 @@ package java.util; -public class LinkedList implements List { +public class LinkedList extends AbstractSequentialList { private Cell front; private Cell rear; private int size; @@ -85,10 +85,6 @@ public class LinkedList implements List { } } - public S[] toArray(S[] a) { - return Collections.toArray(this, a); - } - public int size() { return size; } @@ -97,8 +93,26 @@ public class LinkedList implements List { return find(element) != null; } - public void addAll(Collection c) { - for (T t: c) add(t); + public int indexOf(Object element) { + int i = 0; + for (Cell c = front; c != null; c = c.next) { + if (equal(c.value, element)) { + return i; + } + ++ i; + } + return -1; + } + + public int lastIndexOf(Object element) { + int i = size; + for (Cell c = rear; c != null; c = c.prev) { + -- i; + if (equal(c.value, element)) { + return i; + } + } + return -1; } public boolean add(T element) { @@ -202,7 +216,19 @@ public class LinkedList implements List { } public Iterator iterator() { - return new MyIterator(front); + return listIterator(); + } + + public ListIterator listIterator(int index) { + MyIterator it = new MyIterator(); + for (int i = 0; i < index; ++i) { + it.next(); + } + return it; + } + + public ListIterator listIterator() { + return listIterator(0); } public String toString() { @@ -221,18 +247,29 @@ public class LinkedList implements List { } } - private class MyIterator implements Iterator { + private class MyIterator implements ListIterator { + private Cell toRemove; private Cell current; - private Cell next; - public MyIterator(Cell start) { - next = start; + public T previous() { + if (hasPrevious()) { + T v = current.value; + toRemove = current; + current = current.prev; + return v; + } else { + throw new NoSuchElementException(); + } } public T next() { if (hasNext()) { - current = next; - next = next.next; + if (current == null) { + current = front; + } else { + current = current.next; + } + toRemove = current; return current.value; } else { throw new NoSuchElementException(); @@ -240,13 +277,22 @@ public class LinkedList implements List { } public boolean hasNext() { - return next != null; + if (current == null) { + return front != null; + } else { + return current.next != null; + } + } + + public boolean hasPrevious() { + return current != null; } public void remove() { - if (current != null) { - LinkedList.this.remove(current); - current = null; + if (toRemove != null) { + current = toRemove.prev; + LinkedList.this.remove(toRemove); + toRemove = null; } else { throw new IllegalStateException(); } diff --git a/classpath/java/util/List.java b/classpath/java/util/List.java index 0577e4b3e7..487ebc18d1 100644 --- a/classpath/java/util/List.java +++ b/classpath/java/util/List.java @@ -21,5 +21,13 @@ public interface List extends Collection { public void add(int index, T element); + public int indexOf(Object value); + + public int lastIndexOf(Object value); + public boolean isEmpty(); + + public ListIterator listIterator(int index); + + public ListIterator listIterator(); } diff --git a/classpath/java/util/TreeMap.java b/classpath/java/util/TreeMap.java new file mode 100644 index 0000000000..686e9ffda3 --- /dev/null +++ b/classpath/java/util/TreeMap.java @@ -0,0 +1,205 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util; + +public class TreeMap implements Map { + private TreeSet> set; + + public TreeMap(final Comparator comparator) { + set = new TreeSet(new Comparator>() { + public int compare(MyEntry a, MyEntry b) { + return comparator.compare(a.key, b.key); + } + }); + } + + public TreeMap() { + this(new Comparator() { + public int compare(K a, K b) { + return ((Comparable) a).compareTo(b); + } + }); + } + + public String toString() { + return Collections.toString(this); + } + + public V get(Object key) { + MyEntry e = set.find(new MyEntry(key, null)); + return e == null ? null : e.value; + } + + public V put(K key, V value) { + MyEntry e = set.addAndReplace(new MyEntry(key, value)); + return e == null ? null : e.value; + } + + public void putAll(Map elts) { + for (Map.Entry entry : elts.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + public V remove(Object key) { + MyEntry e = set.removeAndReturn(new MyEntry(key, null)); + return e == null ? null : e.value; + } + + public void clear() { + set.clear(); + } + + public int size() { + return set.size(); + } + + public boolean isEmpty() { + return size() == 0; + } + + public boolean containsKey(Object key) { + return set.contains(new MyEntry(key, null)); + } + + private boolean equal(Object a, Object b) { + return a == null ? b == null : a.equals(b); + } + + public boolean containsValue(Object value) { + for (V v: values()) { + if (equal(v, value)) { + return true; + } + } + return false; + } + + public Set> entrySet() { + return (Set>) (Set) set; + } + + public Set keySet() { + return new KeySet(); + } + + public Collection values() { + return new Values(); + } + + private static class MyEntry implements Entry { + public final K key; + public V value; + + public MyEntry(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + } + + private class KeySet implements Set { + public int size() { + return TreeMap.this.size(); + } + + public boolean isEmpty() { + return TreeMap.this.isEmpty(); + } + + public boolean contains(Object key) { + return containsKey(key); + } + + public boolean add(K key) { + return set.addAndReplace(new MyEntry(key, null)) != null; + } + + public boolean addAll(Collection collection) { + boolean change = false; + for (K k: collection) if (add(k)) change = true; + return change; + } + + public boolean remove(Object key) { + return set.removeAndReturn(new MyEntry(key, null)) != null; + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Collections.toArray(this, array); + } + + public void clear() { + TreeMap.this.clear(); + } + + public Iterator iterator() { + return new Collections.KeyIterator(set.iterator()); + } + } + + private class Values implements Collection { + public int size() { + return TreeMap.this.size(); + } + + public boolean isEmpty() { + return TreeMap.this.isEmpty(); + } + + public boolean contains(Object value) { + return containsValue(value); + } + + public boolean add(V value) { + throw new UnsupportedOperationException(); + } + + public boolean addAll(Collection collection) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object value) { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public T[] toArray(T[] array) { + return Collections.toArray(this, array); + } + + public void clear() { + TreeMap.this.clear(); + } + + public Iterator iterator() { + return new Collections.ValueIterator(set.iterator()); + } + } +} diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 4940eb0d22..5a1d4a8c47 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -10,7 +10,10 @@ package java.util; -public class TreeSet extends AbstractSet implements Collection { +import avian.PersistentSet; +import avian.Cell; + +public class TreeSet extends AbstractSet implements Collection { private PersistentSet> set; private int size; @@ -49,34 +52,52 @@ public class TreeSet extends AbstractSet implements Collection { return false; } - // Used by hashMaps for replacement - public void addAndReplace(T value) { + T addAndReplace(T value) { PersistentSet.Path> p = set.find(new Cell(value, null)); if (p.fresh()) { set = p.add(); ++size; + return null; } else { + T old = p.value().value; set = p.replaceWith(new Cell(value, null)); + return old; } } - public boolean remove(Object value) { + T find(T value) { + PersistentSet.Path> p = set.find(new Cell(value, null)); + return p.fresh() ? null : p.value().value; + } + + T removeAndReturn(T value) { + Cell cell = removeCell(value); + return cell == null ? null : cell.value; + } + + private Cell removeCell(Object value) { PersistentSet.Path> p = set.find(new Cell(value, null)); if (p.fresh()) { - return false; + return null; } else { --size; + Cell old = p.value(); + if (p.value().next != null) { set = p.replaceWith(p.value().next); } else { set = p.remove(); } - return true; + return old; } } + public boolean remove(Object value) { + return removeCell(value) != null; + } + public int size() { return size; } diff --git a/classpath/java/util/Vector.java b/classpath/java/util/Vector.java index f06f58ecbc..c23a4d59fd 100644 --- a/classpath/java/util/Vector.java +++ b/classpath/java/util/Vector.java @@ -10,7 +10,7 @@ package java.util; -public class Vector implements List { +public class Vector extends AbstractList { private final ArrayList list; public Vector(int capacity) { @@ -77,10 +77,6 @@ public class Vector implements List { return list.isEmpty(); } - public synchronized S[] toArray(S[] a) { - return list.toArray(a); - } - public void removeElementAt(int index) { remove(index); } @@ -97,11 +93,11 @@ public class Vector implements List { list.clear(); } - public synchronized int indexOf(T element) { + public synchronized int indexOf(Object element) { return list.indexOf(element); } - public synchronized int lastIndexOf(T element) { + public synchronized int lastIndexOf(Object element) { return list.lastIndexOf(element); } @@ -112,7 +108,15 @@ public class Vector implements List { } public Iterator iterator() { - return new Collections.ArrayListIterator(this); + return listIterator(); + } + + public ListIterator listIterator(int index) { + return new Collections.ArrayListIterator(this, index); + } + + public ListIterator listIterator() { + return listIterator(0); } public Enumeration elements() { diff --git a/classpath/java/util/concurrent/Callable.java b/classpath/java/util/concurrent/Callable.java new file mode 100644 index 0000000000..f0ad3be39e --- /dev/null +++ b/classpath/java/util/concurrent/Callable.java @@ -0,0 +1,15 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.util.concurrent; + +public interface Callable { + public T call() throws Exception; +} diff --git a/classpath/java/util/logging/Logger.java b/classpath/java/util/logging/Logger.java index ab1a1bd28a..d06a7bb00a 100644 --- a/classpath/java/util/logging/Logger.java +++ b/classpath/java/util/logging/Logger.java @@ -113,8 +113,9 @@ public class Logger { if (level.intValue() < getEffectiveLevel().intValue()) { return; } - LogRecord r = new LogRecord(name, caller.getName(), level, message, - exception); + LogRecord r = new LogRecord + (name, caller == null ? "" : caller.getName(), level, message, + exception); publish(r); } diff --git a/classpath/java/util/zip/ZipFile.java b/classpath/java/util/zip/ZipFile.java index a11a3c5dd1..d59885496f 100644 --- a/classpath/java/util/zip/ZipFile.java +++ b/classpath/java/util/zip/ZipFile.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2009, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -63,7 +63,7 @@ public class ZipFile { return index.size(); } - public Enumeration entries() { + public Enumeration entries() { return new MyEnumeration(window, index.values().iterator()); } diff --git a/makefile b/makefile index b9f489c347..21b007dd13 100644 --- a/makefile +++ b/makefile @@ -14,7 +14,9 @@ build-platform = \ | sed 's/^cygwin.*$$/cygwin/') arch = $(build-arch) -platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) +bootimage-platform = \ + $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) +platform = $(bootimage-platform) mode = fast process = compile @@ -31,6 +33,29 @@ endif ifeq ($(heapdump),true) options := $(options)-heapdump endif +ifeq ($(tails),true) + options := $(options)-tails +endif +ifeq ($(continuations),true) + options := $(options)-continuations +endif +ifdef gnu + options := $(options)-gnu + gnu-sources = $(src)/gnu.cpp + gnu-jar = $(gnu)/share/classpath/glibj.zip + gnu-libraries = \ + $(gnu)/lib/classpath/libjavaio.a \ + $(gnu)/lib/classpath/libjavalang.a \ + $(gnu)/lib/classpath/libjavalangreflect.a \ + $(gnu)/lib/classpath/libjavamath.a \ + $(gnu)/lib/classpath/libjavanet.a \ + $(gnu)/lib/classpath/libjavanio.a \ + $(gnu)/lib/classpath/libjavautil.a + gnu-object-dep = $(build)/gnu-object.dep + gnu-cflags = -DBOOT_BUILTINS=\"javaio,javalang,javalangreflect,javamath,javanet,javanio,javautil\" -DAVIAN_GNU + gnu-lflags = -lgmp + gnu-objects = $(shell find $(build)/gnu-objects -name "*.o") +endif root = $(shell (cd .. && pwd)) build = build @@ -41,6 +66,12 @@ src = src classpath = classpath test = test +ifdef gnu + avian-classpath-build = $(build)/avian-classpath +else + avian-classpath-build = $(classpath-build) +endif + input = List build-cxx = g++ @@ -71,13 +102,14 @@ warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self \ common-cflags = $(warnings) -fno-rtti -fno-exceptions -fno-omit-frame-pointer \ "-I$(JAVA_HOME)/include" -idirafter $(src) -I$(native-build) \ -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ + $(gnu-cflags) build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread cflags = $(build-cflags) -common-lflags = -lm -lz +common-lflags = -lm -lz $(gnu-lflags) build-lflags = @@ -111,7 +143,7 @@ endif ifeq ($(platform),darwin) build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) - lflags = $(common-lflags) -ldl -framework CoreFoundation + lflags = $(common-lflags) -ldl -framework CoreFoundation -framework CoreServices ifeq ($(bootimage),true) bootimage-lflags = -Wl,-segprot,__BOOT,rwx,rwx endif @@ -166,7 +198,6 @@ ifeq ($(platform),windows) strip = : inc = "$(root)/win64/include" lib = "$(root)/win64/lib" - cflags += -D__x86_64__ -D__WINDOWS__ pointer-size = 8 object-format = pe-x86-64 endif @@ -236,7 +267,8 @@ vm-sources = \ $(src)/$(process).cpp \ $(src)/builtin.cpp \ $(src)/jnienv.cpp \ - $(src)/process.cpp + $(src)/process.cpp \ + $(gnu-sources) vm-asm-sources = $(src)/$(asm).S @@ -266,11 +298,20 @@ ifeq ($(heapdump),true) cflags += -DAVIAN_HEAPDUMP endif +ifeq ($(tails),true) + cflags += -DAVIAN_TAILS +endif + +ifeq ($(continuations),true) + cflags += -DAVIAN_CONTINUATIONS + asmflags += -DAVIAN_CONTINUATIONS +endif + bootimage-generator-sources = $(src)/bootimage.cpp bootimage-generator-objects = \ $(call cpp-objects,$(bootimage-generator-sources),$(src),$(native-build)) bootimage-generator = \ - $(build)/$(build-platform)-$(build-arch)$(options)/bootimage-generator + $(build)/$(bootimage-platform)-$(build-arch)$(options)/bootimage-generator bootimage-bin = $(native-build)/bootimage.bin bootimage-object = $(native-build)/bootimage-bin.o @@ -309,11 +350,43 @@ classpath-sources = $(shell find $(classpath) -name '*.java') classpath-classes = \ $(call java-classes,$(classpath-sources),$(classpath),$(classpath-build)) classpath-object = $(native-build)/classpath-jar.o -classpath-dep = $(classpath-build)/dep +classpath-dep = $(classpath-build).dep + +gnu-blacklist = \ + java/lang/AbstractStringBuffer.class \ + java/lang/reflect/Proxy.class + +gnu-overrides = \ + avian/*.class \ + avian/resource/*.class \ + java/lang/Class.class \ + java/lang/Enum.class \ + java/lang/InheritableThreadLocal.class \ + java/lang/Object.class \ + java/lang/StackTraceElement.class \ + java/lang/String*.class \ + java/lang/StringBuffer.class \ + java/lang/StringBuilder.class \ + java/lang/Thread.class \ + java/lang/ThreadLocal.class \ + java/lang/Throwable.class \ + java/lang/ref/PhantomReference.class \ + java/lang/ref/Reference.class \ + java/lang/ref/ReferenceQueue.class \ + java/lang/ref/WeakReference.class \ + java/lang/reflect/AccessibleObject.class \ + java/lang/reflect/Constructor.class \ + java/lang/reflect/Field.class \ + java/lang/reflect/Method.class test-sources = $(wildcard $(test)/*.java) test-classes = $(call java-classes,$(test-sources),$(test),$(test-build)) -test-dep = $(test-build)/dep +test-dep = $(test-build).dep + +test-extra-sources = $(wildcard $(test)/extra/*.java) +test-extra-classes = \ + $(call java-classes,$(test-extra-sources),$(test),$(test-build)) +test-extra-dep = $(test-build)-extra.dep class-name = $(patsubst $(1)/%.class,%,$(2)) class-names = $(foreach x,$(2),$(call class-name,$(1),$(x))) @@ -324,7 +397,7 @@ args = $(flags) $(input) .PHONY: build build: $(static-library) $(executable) $(dynamic-library) \ - $(executable-dynamic) $(classpath-dep) $(test-dep) + $(executable-dynamic) $(classpath-dep) $(test-dep) $(test-extra-dep) $(test-classes): $(classpath-dep) @@ -355,7 +428,7 @@ tarball: .PHONY: javadoc javadoc: - javadoc -sourcepath classpath -d build/javadoc -subpackages java \ + javadoc -sourcepath classpath -d build/javadoc -subpackages avian:java \ -windowtitle "Avian v$(version) Class Library API" \ -doctitle "Avian v$(version) Class Library API" \ -header "Avian v$(version)" \ @@ -383,11 +456,24 @@ $(native-build)/type-generator.o: \ $(classpath-build)/%.class: $(classpath)/%.java @echo $(<) -$(classpath-dep): $(classpath-sources) +$(classpath-dep): $(classpath-sources) $(gnu-jar) @echo "compiling classpath classes" - @mkdir -p $(dir $(@)) - $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ + @mkdir -p $(avian-classpath-build) + $(javac) -d $(avian-classpath-build) \ + -bootclasspath $(avian-classpath-build) \ $(shell $(MAKE) -s --no-print-directory $(classpath-classes)) +ifdef gnu + (wd=$$(pwd) && \ + cd $(avian-classpath-build) && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(build)/overrides.jar")" \ + $(gnu-overrides)) + @mkdir -p $(classpath-build) + (wd=$$(pwd) && \ + cd $(classpath-build) && \ + $(jar) xf $(gnu-jar) && \ + rm $(gnu-blacklist) && \ + jar xf "$$($(native-path) "$${wd}/$(build)/overrides.jar")") +endif @touch $(@) $(test-build)/%.class: $(test)/%.java @@ -395,13 +481,20 @@ $(test-build)/%.class: $(test)/%.java $(test-dep): $(test-sources) @echo "compiling test classes" - @mkdir -p $(dir $(@)) - $(javac) -d $(dir $(@)) -bootclasspath $(classpath-build) \ + @mkdir -p $(test-build) + $(javac) -d $(test-build) -bootclasspath $(classpath-build) \ $(shell $(MAKE) -s --no-print-directory $(test-classes)) - $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(dir $(@)) \ + $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ test/Subroutine.java @touch $(@) +$(test-extra-dep): $(test-extra-sources) + @echo "compiling extra test classes" + @mkdir -p $(test-build) + $(javac) -d $(test-build) -bootclasspath $(classpath-build) \ + $(shell $(MAKE) -s --no-print-directory $(test-extra-classes)) + @touch $(@) + define compile-object @echo "compiling $(@)" @mkdir -p $(dir $(@)) @@ -411,7 +504,7 @@ endef define compile-asm-object @echo "compiling $(@)" @mkdir -p $(dir $(@)) - $(cc) $(cflags) -I$(src) -c $(<) -o $(@) + $(cc) -I$(src) $(asmflags) -c $(<) -o $(@) endef $(vm-cpp-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) @@ -439,9 +532,9 @@ $(boot-object): $(boot-source) $(compile-object) $(build)/classpath.jar: $(classpath-dep) - (wd=$$(pwd); \ - cd $(classpath-build); \ - $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" $$(find . -name '*.class')) + (wd=$$(pwd) && \ + cd $(classpath-build) && \ + $(jar) c0f "$$($(native-path) "$${wd}/$(@)")" .) $(binaryToMacho): $(src)/binaryToMacho.cpp $(cxx) $(^) -o $(@) @@ -452,8 +545,8 @@ ifeq ($(platform),darwin) $(binaryToMacho) $(asm) $(build)/classpath.jar __TEXT __text \ __binary_classpath_jar_start __binary_classpath_jar_end > $(@) else - (wd=$$(pwd); \ - cd $(build); \ + (wd=$$(pwd) && \ + cd $(build) && \ $(objcopy) -I binary classpath.jar \ -O $(object-format) -B $(object-arch) "$${wd}/$(@)") endif @@ -467,14 +560,15 @@ $(generator-objects): $(native-build)/%.o: $(src)/%.cpp $(jni-objects): $(native-build)/%.o: $(classpath)/%.cpp $(compile-object) +$(static-library): $(gnu-object-dep) $(static-library): $(vm-objects) $(jni-objects) $(vm-heapwalk-objects) @echo "creating $(@)" rm -rf $(@) - $(ar) cru $(@) $(^) + $(ar) cru $(@) $(^) $(call gnu-objects) $(ranlib) $(@) $(bootimage-bin): $(bootimage-generator) - $(<) $(classpath-build) > $(@) + $(<) $(classpath-build) $(@) $(bootimage-object): $(bootimage-bin) $(binaryToMacho) @echo "creating $(@)" @@ -482,24 +576,32 @@ ifeq ($(platform),darwin) $(binaryToMacho) $(asm) $(<) __BOOT __boot \ __binary_bootimage_bin_start __binary_bootimage_bin_end > $(@) else - (wd=$$(pwd); \ - cd $(native-build); \ + (wd=$$(pwd) && \ + cd $(native-build) && \ $(objcopy) --rename-section=.data=.boot -I binary bootimage.bin \ - -O $(object-format) -B $(object-arch) "$${wd}/$(@).tmp"; \ + -O $(object-format) -B $(object-arch) "$${wd}/$(@).tmp" && \ $(objcopy) --set-section-flags .boot=alloc,load,code "$${wd}/$(@).tmp" \ "$${wd}/$(@)") endif +$(gnu-object-dep): $(gnu-libraries) + @mkdir -p $(build)/gnu-objects + (cd $(build)/gnu-objects && \ + for x in $(gnu-libraries); do ar x $${x}; done) + @touch $(@) + +$(executable): $(gnu-object-dep) $(executable): \ $(vm-objects) $(jni-objects) $(driver-object) $(vm-heapwalk-objects) \ $(boot-object) $(vm-classpath-object) @echo "linking $(@)" ifeq ($(platform),windows) - $(dlltool) -z $(@).def $(^) + $(dlltool) -z $(@).def $(^) $(call gnu-objects) $(dlltool) -d $(@).def -e $(@).exp - $(cc) $(@).exp $(^) $(lflags) -o $(@) + $(cc) $(@).exp $(^) $(call gnu-objects) $(lflags) -o $(@) else - $(cc) $(^) $(rdynamic) $(lflags) $(bootimage-lflags) -o $(@) + $(cc) $(^) $(call gnu-objects) $(rdynamic) $(lflags) $(bootimage-lflags) \ + -o $(@) endif $(strip) $(strip-all) $(@) @@ -509,7 +611,7 @@ make-bootimage-generator: (unset MAKEFLAGS && \ make mode=$(mode) \ arch=$(build-arch) \ - platform=$(build-platform) \ + platform=$(bootimage-platform) \ bootimage=$(bootimage) \ heapdump=$(heapdump) \ bootimage-generator= \ @@ -528,11 +630,13 @@ else $(cc) $(^) $(rdynamic) $(lflags) -o $(@) endif +$(dynamic-library): $(gnu-object-dep) $(dynamic-library): \ $(vm-objects) $(dynamic-object) $(jni-objects) $(vm-heapwalk-objects) \ - $(boot-object) $(vm-classpath-object) + $(boot-object) $(vm-classpath-object) $(gnu-libraries) @echo "linking $(@)" - $(cc) $(^) $(shared) $(lflags) $(bootimage-lflags) -o $(@) + $(cc) $(^) $(call gnu-objects) $(shared) $(lflags) $(bootimage-lflags) \ + -o $(@) $(strip) $(strip-all) $(@) $(executable-dynamic): $(driver-dynamic-object) $(dynamic-library) diff --git a/readme.txt b/readme.txt index 49374fe80c..ccf4be24d6 100644 --- a/readme.txt +++ b/readme.txt @@ -76,7 +76,8 @@ certain flags described below, all of which are optional. $ make platform={linux,windows,darwin} arch={i386,x86_64,powerpc} \ process={compile,interpret} mode={debug,debug-fast,fast,small} \ - bootimage={true,false} heapdump={true,false} + bootimage={true,false} heapdump={true,false} tails={true,false} \ + continuations={true,false} * platform - the target platform default: output of $(uname -s | tr [:upper:] [:lower:]), @@ -86,7 +87,7 @@ certain flags described below, all of which are optional. default: output of $(uname -m), normalized in some cases (e.g. i686 -> i386) - * mode - which set of compilation flags to use, which determine + * mode - which set of compilation flags to use to determine optimization level, debug symbols, and whether to enable assertions default: fast @@ -105,6 +106,18 @@ certain flags described below, all of which are optional. heapdump.cpp for details. default: false + * tails - if true, optimize each tail call by replacing the caller's + stack frame with the callee's. This convention ensures proper + tail recursion, suitable for languages such as Scheme. This + option is only valid for process=compile builds. + default: false + + * continuations - if true, support continuations via the + avian.Continuations methods callWithCurrentContinuation and + dynamicWind. See Continuations.java for details. This option is + only valid for process=compile builds. + default: false + 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 @@ -390,12 +403,12 @@ Step 5: Run ProGuard with stage1 as input and stage2 as output. $ java -jar ../../proguard4.3/lib/proguard.jar \ -injars stage1 -outjars stage2 @../vm.pro @hello.pro -(note: pass -dontusemixedcaseclassnames to ProGuard when building on systems with case-insensitive filesystems such as Windows and OS X) +(note: pass -dontusemixedcaseclassnames to ProGuard when building on +systems with case-insensitive filesystems such as Windows and OS X) Step 6: Build the boot image. - $ ../build/linux-i386-bootimage/bootimage-generator stage2 \ - > bootimage.bin + $ ../build/linux-i386-bootimage/bootimage-generator stage2 bootimage.bin Step 7: Make an object file out of the boot image. diff --git a/src/arch.h b/src/arch.h index ae613297b6..ae2039296f 100644 --- a/src/arch.h +++ b/src/arch.h @@ -14,7 +14,8 @@ #include "common.h" extern "C" void NO_RETURN -vmJump(void* address, void* base, void* stack, void* thread); +vmJump(void* address, void* base, void* stack, void* thread, + uintptr_t returnLow, uintptr_t returnHigh); #if (defined __i386__) || (defined __x86_64__) # include "x86.h" diff --git a/src/assembler.h b/src/assembler.h index 78a807ebd6..8b0809f165 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -16,6 +16,12 @@ namespace vm { +#ifdef AVIAN_TAILS +const bool TailCalls = true; +#else +const bool TailCalls = false; +#endif + enum Operation { Return, LoadBarrier, @@ -31,13 +37,20 @@ enum UnaryOperation { AlignedCall, Jump, LongJump, + AlignedJump, JumpIfLess, JumpIfGreater, JumpIfLessOrEqual, JumpIfGreaterOrEqual, JumpIfEqual, JumpIfNotEqual, - JumpIfUnordered + JumpIfFloatUnordered, + JumpIfFloatLess, + JumpIfFloatGreater, + JumpIfFloatLessOrEqual, + JumpIfFloatGreaterOrEqual, + JumpIfFloatEqual, + JumpIfFloatNotEqual, }; const unsigned UnaryOperationCount = JumpIfNotEqual + 1; @@ -225,12 +238,6 @@ class DelayedPromise: public ListenPromise { DelayedPromise* next; }; -class TraceHandler { - public: - virtual void handleTrace(Promise* address, unsigned padIndex, - unsigned padding) = 0; -}; - class Assembler { public: class Operand { }; @@ -259,7 +266,7 @@ class Assembler { class Memory: public Operand { public: - Memory(int base, int offset, int index = NoRegister, unsigned scale = 0): + Memory(int base, int offset, int index = NoRegister, unsigned scale = 1): base(base), offset(offset), index(index), scale(scale) { } @@ -290,22 +297,33 @@ class Assembler { virtual unsigned floatRegisterCount() = 0; virtual uint64_t generalRegisters() = 0; virtual uint64_t floatRegisters() = 0; + virtual uint64_t allRegisters() = 0; virtual int stack() = 0; virtual int thread() = 0; virtual int returnLow() = 0; virtual int returnHigh() = 0; + virtual int virtualCallTarget() = 0; + virtual int virtualCallIndex() = 0; virtual bool bigEndian() = 0; virtual bool supportsFloatCompare(unsigned size) = 0; + virtual bool alwaysCondensed(BinaryOperation op) = 0; + virtual bool alwaysCondensed(TernaryOperation op) = 0; + virtual bool reserved(int register_) = 0; + virtual unsigned frameFootprint(unsigned footprint) = 0; virtual unsigned argumentFootprint(unsigned footprint) = 0; virtual unsigned argumentRegisterCount() = 0; virtual int argumentRegister(unsigned index) = 0; + virtual unsigned stackAlignmentInWords() = 0; + + virtual bool matchCall(void* returnAddress, void* target) = 0; + virtual void updateCall(UnaryOperation op, bool assertAlignment, void* returnAddress, void* newTarget) = 0; @@ -318,6 +336,8 @@ class Assembler { virtual unsigned frameHeaderSize() = 0; virtual unsigned frameReturnAddressSize() = 0; virtual unsigned frameFooterSize() = 0; + virtual int returnAddressOffset() = 0; + virtual int framePointerOffset() = 0; virtual void nextFrame(void** stack, void** base) = 0; virtual BinaryOperation hasBinaryIntrinsic(Thread* t, object method) = 0; @@ -361,7 +381,15 @@ class Assembler { virtual void saveFrame(unsigned stackOffset, unsigned baseOffset) = 0; virtual void pushFrame(unsigned argumentCount, ...) = 0; virtual void allocateFrame(unsigned footprint) = 0; + virtual void adjustFrame(unsigned footprint) = 0; virtual void popFrame() = 0; + virtual void popFrameForTailCall(unsigned footprint, int offset, + int returnAddressSurrogate, + int framePointerSurrogate) = 0; + virtual void popFrameAndPopArgumentsAndReturn(unsigned argumentFootprint) + = 0; + virtual void popFrameAndUpdateStackAndReturn(unsigned stackOffsetFromThread) + = 0; virtual void apply(Operation op) = 0; diff --git a/src/bootimage.cpp b/src/bootimage.cpp index f2662631e1..6df24a6ff3 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -33,12 +33,9 @@ endsWith(const char* suffix, const char* s, unsigned length) object makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, - unsigned capacity, uintptr_t* codeMap, const char* className, + uintptr_t* codeMap, const char* className, const char* methodName, const char* methodSpec) { - unsigned size = 0; - t->m->processor->compileThunks(t, image, code, &size, capacity); - object constants = 0; PROTECT(t, constants); @@ -79,8 +76,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, == 0))) { t->m->processor->compileMethod - (t, zone, code, &size, capacity, &constants, &calls, &addresses, - method); + (t, zone, &constants, &calls, &addresses, method); } } } @@ -119,8 +115,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, - reinterpret_cast(code)); } - image->codeSize = size; - return constants; } @@ -144,7 +138,7 @@ visitRoots(Thread* t, BootImage* image, HeapWalker* w, object constants) image->loader = w->visitRoot(m->loader); image->types = w->visitRoot(m->types); - m->processor->visitRoots(image, w); + m->processor->visitRoots(w); for (; constants; constants = tripleThird(t, constants)) { w->visitRoot(tripleFirst(t, constants)); @@ -187,7 +181,7 @@ makeHeapImage(Thread* t, BootImage* image, uintptr_t* heap, uintptr_t* map, and (currentOffset * BytesPerWord) == ClassStaticTable) { FixedAllocator allocator - (t, reinterpret_cast(heap + position), + (t->m->system, reinterpret_cast(heap + position), (capacity - position) * BytesPerWord); unsigned totalInBytes; @@ -285,21 +279,18 @@ offset(object a, uintptr_t* b) } void -writeBootImage(Thread* t, FILE* out, const char* className, +writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code, + unsigned codeCapacity, const char* className, const char* methodName, const char* methodSpec) { Zone zone(t->m->system, t->m->heap, 64 * 1024); - BootImage image; - const unsigned CodeCapacity = 32 * 1024 * 1024; - uint8_t* code = static_cast(t->m->heap->allocate(CodeCapacity)); uintptr_t* codeMap = static_cast - (t->m->heap->allocate(codeMapSize(CodeCapacity))); - memset(codeMap, 0, codeMapSize(CodeCapacity)); + (t->m->heap->allocate(codeMapSize(codeCapacity))); + memset(codeMap, 0, codeMapSize(codeCapacity)); object constants = makeCodeImage - (t, &zone, &image, code, CodeCapacity, codeMap, className, methodName, - methodSpec); + (t, &zone, image, code, codeMap, className, methodName, methodSpec); if (t->exception) return; @@ -312,16 +303,20 @@ writeBootImage(Thread* t, FILE* out, const char* className, (t->m->heap->allocate(heapMapSize(HeapCapacity))); memset(heapMap, 0, heapMapSize(HeapCapacity)); + // this map will not be used when the bootimage is loaded, so + // there's no need to preserve it: + t->m->byteArrayMap = makeWeakHashMap(t, 0, 0); + collect(t, Heap::MajorCollection); HeapWalker* heapWalker = makeHeapImage - (t, &image, heap, heapMap, HeapCapacity, constants); + (t, image, heap, heapMap, HeapCapacity, constants); updateConstants(t, constants, code, codeMap, heapWalker->map()); - image.classCount = hashMapSize(t, t->m->classMap); + image->classCount = hashMapSize(t, t->m->classMap); unsigned* classTable = static_cast - (t->m->heap->allocate(image.classCount * sizeof(unsigned))); + (t->m->heap->allocate(image->classCount * sizeof(unsigned))); { unsigned i = 0; for (HashMapIterator it(t, t->m->classMap); it.hasMore();) { @@ -329,9 +324,9 @@ writeBootImage(Thread* t, FILE* out, const char* className, } } - image.stringCount = hashMapSize(t, t->m->stringMap); + image->stringCount = hashMapSize(t, t->m->stringMap); unsigned* stringTable = static_cast - (t->m->heap->allocate(image.stringCount * sizeof(unsigned))); + (t->m->heap->allocate(image->stringCount * sizeof(unsigned))); { unsigned i = 0; for (HashMapIterator it(t, t->m->stringMap); it.hasMore();) { @@ -340,29 +335,28 @@ writeBootImage(Thread* t, FILE* out, const char* className, } } - unsigned* callTable = t->m->processor->makeCallTable - (t, &image, heapWalker, code); + unsigned* callTable = t->m->processor->makeCallTable(t, heapWalker); heapWalker->dispose(); - image.magic = BootImage::Magic; - image.codeBase = reinterpret_cast(code); + image->magic = BootImage::Magic; + image->codeBase = reinterpret_cast(code); fprintf(stderr, "class count %d string count %d call count %d\n" "heap size %d code size %d\n", - image.classCount, image.stringCount, image.callCount, image.heapSize, - image.codeSize); + image->classCount, image->stringCount, image->callCount, + image->heapSize, image->codeSize); if (true) { - fwrite(&image, sizeof(BootImage), 1, out); + fwrite(image, sizeof(BootImage), 1, out); - fwrite(classTable, image.classCount * sizeof(unsigned), 1, out); - fwrite(stringTable, image.stringCount * sizeof(unsigned), 1, out); - fwrite(callTable, image.callCount * sizeof(unsigned) * 2, 1, out); + fwrite(classTable, image->classCount * sizeof(unsigned), 1, out); + fwrite(stringTable, image->stringCount * sizeof(unsigned), 1, out); + fwrite(callTable, image->callCount * sizeof(unsigned) * 2, 1, out); - unsigned offset = (image.classCount * sizeof(unsigned)) - + (image.stringCount * sizeof(unsigned)) - + (image.callCount * sizeof(unsigned) * 2); + unsigned offset = (image->classCount * sizeof(unsigned)) + + (image->stringCount * sizeof(unsigned)) + + (image->callCount * sizeof(unsigned) * 2); while (offset % BytesPerWord) { uint8_t c = 0; @@ -370,11 +364,11 @@ writeBootImage(Thread* t, FILE* out, const char* className, ++ offset; } - fwrite(heapMap, pad(heapMapSize(image.heapSize)), 1, out); - fwrite(heap, pad(image.heapSize), 1, out); + fwrite(heapMap, pad(heapMapSize(image->heapSize)), 1, out); + fwrite(heap, pad(image->heapSize), 1, out); - fwrite(codeMap, pad(codeMapSize(image.codeSize)), 1, out); - fwrite(code, pad(image.codeSize), 1, out); + fwrite(codeMap, pad(codeMapSize(image->codeSize)), 1, out); + fwrite(code, pad(image->codeSize), 1, out); } } @@ -383,8 +377,8 @@ writeBootImage(Thread* t, FILE* out, const char* className, int main(int ac, const char** av) { - if (ac < 2 or ac > 5) { - fprintf(stderr, "usage: %s " + if (ac < 3 or ac > 6) { + fprintf(stderr, "usage: %s " "[ [ []]]\n", av[0]); return -1; } @@ -393,15 +387,29 @@ main(int ac, const char** av) Heap* h = makeHeap(s, 128 * 1024 * 1024); Finder* f = makeFinder(s, av[1], 0); Processor* p = makeProcessor(s, h); + + BootImage image; + const unsigned CodeCapacity = 32 * 1024 * 1024; + uint8_t* code = static_cast(h->allocate(CodeCapacity)); + p->initialize(&image, code, CodeCapacity); + Machine* m = new (h->allocate(sizeof(Machine))) Machine(s, h, f, p, 0, 0); Thread* t = p->makeThread(m, 0, 0); enter(t, Thread::ActiveState); enter(t, Thread::IdleState); + FILE* output = fopen(av[2], "wb"); + if (output == 0) { + fprintf(stderr, "unable to open %s\n", av[2]); + return -1; + } + writeBootImage - (t, stdout, (ac > 2 ? av[2] : 0), (ac > 3 ? av[3] : 0), - (ac > 4 ? av[4] : 0)); + (t, output, &image, code, CodeCapacity, + (ac > 3 ? av[3] : 0), (ac > 4 ? av[4] : 0), (ac > 5 ? av[5] : 0)); + + fclose(output); if (t->exception) { printTrace(t, t->exception); diff --git a/src/bootimage.h b/src/bootimage.h index ce83611583..4472ac28ae 100644 --- a/src/bootimage.h +++ b/src/bootimage.h @@ -39,10 +39,12 @@ class BootImage { unsigned types; unsigned methodTree; unsigned methodTreeSentinal; + unsigned virtualThunks; uintptr_t codeBase; unsigned defaultThunk; + unsigned defaultVirtualThunk; unsigned nativeThunk; unsigned aioobThunk; @@ -50,6 +52,7 @@ class BootImage { unsigned thunkSize; unsigned compileMethodCall; + unsigned compileVirtualMethodCall; unsigned invokeNativeCall; unsigned throwArrayIndexOutOfBoundsCall; diff --git a/src/builtin.cpp b/src/builtin.cpp index cf48ee58ef..712dabce0f 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -22,14 +22,16 @@ replace(char a, char b, char* c) for (; *c; ++c) if (*c == a) *c = b; } -jclass -search(Thread* t, jstring name, object (*op)(Thread*, object), +int64_t +search(Thread* t, object name, object (*op)(Thread*, object), bool replaceDots) { if (LIKELY(name)) { - object n = makeByteArray(t, stringLength(t, *name) + 1); + PROTECT(t, name); + + object n = makeByteArray(t, stringLength(t, name) + 1); char* s = reinterpret_cast(&byteArrayBody(t, n, 0)); - stringChars(t, *name, s); + stringChars(t, name, s); if (replaceDots) { replace('.', '/', s); @@ -40,7 +42,7 @@ search(Thread* t, jstring name, object (*op)(Thread*, object), return 0; } - return makeLocalReference(t, r); + return reinterpret_cast(r); } else { t->exception = makeNullPointerException(t); return 0; @@ -61,126 +63,159 @@ enumerateThreads(Thread* t, Thread* x, object array, unsigned* index, } } +bool +compatibleArrayTypes(Thread* t, object a, object b) +{ + return classArrayElementSize(t, a) + and classArrayElementSize(t, b) + and (a == b + or (not ((classVmFlags(t, a) & PrimitiveFlag) + or (classVmFlags(t, b) & PrimitiveFlag)))); +} + +void +runOnLoadIfFound(Thread* t, System::Library* library) +{ + void* p = library->resolve("JNI_OnLoad"); + if (p) { + jint (JNICALL * JNI_OnLoad)(Machine*, void*); + memcpy(&JNI_OnLoad, &p, sizeof(void*)); + JNI_OnLoad(t->m, 0); + } +} + } // namespace -extern "C" JNIEXPORT jstring JNICALL -Java_java_lang_Object_toString(Thread* t, jobject this_) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_toString +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - unsigned hash = objectHash(t, *this_); + unsigned hash = objectHash(t, this_); object s = makeString (t, "%s@0x%x", - &byteArrayBody(t, className(t, objectClass(t, *this_)), 0), + &byteArrayBody(t, className(t, objectClass(t, this_)), 0), hash); - return makeLocalReference(t, s); + return reinterpret_cast(s); } -extern "C" JNIEXPORT jclass JNICALL -Java_java_lang_Object_getClass(Thread* t, jobject this_) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_getClass +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - return makeLocalReference(t, objectClass(t, *this_)); + return reinterpret_cast(objectClass(t, this_)); } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Object_wait(Thread* t, jobject this_, jlong milliseconds) +Avian_java_lang_Object_wait +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); + int64_t milliseconds; memcpy(&milliseconds, arguments + 1, 8); - vm::wait(t, *this_, milliseconds); + vm::wait(t, this_, milliseconds); } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Object_notify(Thread* t, jobject this_) +Avian_java_lang_Object_notify +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - notify(t, *this_); + notify(t, this_); } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Object_notifyAll(Thread* t, jobject this_) +Avian_java_lang_Object_notifyAll +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - notifyAll(t, *this_); + notifyAll(t, this_); } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_Object_hashCode(Thread* t, jobject this_) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_hashCode +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - return objectHash(t, *this_); + return objectHash(t, this_); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_Object_clone(Thread* t, jclass, jobject o) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_clone +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object o = reinterpret_cast(arguments[0]); + PROTECT(t, o); - object class_ = objectClass(t, *o); - unsigned size = baseSize(t, *o, class_) * BytesPerWord; + object class_ = objectClass(t, o); + unsigned size = baseSize(t, o, class_) * BytesPerWord; object clone; if (classArrayElementSize(t, class_)) { clone = static_cast(allocate(t, size, classObjectMask(t, class_))); - memcpy(clone, *o, size); + memcpy(clone, o, size); // clear any object header flags: - setObjectClass(t, *o, objectClass(t, *o)); + setObjectClass(t, o, objectClass(t, o)); } else { clone = make(t, class_); memcpy(reinterpret_cast(clone) + 1, - reinterpret_cast(*o) + 1, + reinterpret_cast(o) + 1, size - BytesPerWord); } - return makeLocalReference(t, clone); + return reinterpret_cast(clone); } -extern "C" JNIEXPORT jclass JNICALL -Java_java_lang_ClassLoader_defineClass -(Thread* t, jclass, jbyteArray b, jint offset, jint length) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_ClassLoader_defineClass +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object b = reinterpret_cast(arguments[0]); + int offset = arguments[1]; + int length = arguments[2]; uint8_t* buffer = static_cast (t->m->heap->allocate(length)); - memcpy(buffer, &byteArrayBody(t, *b, offset), length); + memcpy(buffer, &byteArrayBody(t, b, offset), length); object c = parseClass(t, buffer, length); t->m->heap->free(buffer, length); - return makeLocalReference(t, c); + return reinterpret_cast(c); } -extern "C" JNIEXPORT jclass JNICALL -Java_java_lang_SystemClassLoader_findLoadedClass -(Thread* t, jclass, jstring name) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findLoadedClass +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object name = reinterpret_cast(arguments[1]); return search(t, name, findLoadedClass, true); } -extern "C" JNIEXPORT jclass JNICALL -Java_java_lang_SystemClassLoader_findClass(Thread* t, jclass, jstring name) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findClass +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object name = reinterpret_cast(arguments[1]); return search(t, name, resolveClass, true); } -extern "C" JNIEXPORT jboolean JNICALL -Java_java_lang_SystemClassLoader_resourceExists -(Thread* t, jclass, jstring name) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_resourceExists +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object name = reinterpret_cast(arguments[1]); if (LIKELY(name)) { - char n[stringLength(t, *name) + 1]; - stringChars(t, *name, n); + char n[stringLength(t, name) + 1]; + stringChars(t, name, n); return t->m->finder->exists(n); } else { t->exception = makeNullPointerException(t); @@ -188,38 +223,49 @@ Java_java_lang_SystemClassLoader_resourceExists } } -extern "C" JNIEXPORT jobject JNICALL -Java_java_io_ObjectInputStream_makeInstance(Thread* t, jclass, jclass c) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_io_ObjectInputStream_makeInstance +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object c = reinterpret_cast(arguments[0]); - return makeLocalReference(t, make(t, *c)); + return reinterpret_cast(make(t, c)); } -extern "C" JNIEXPORT jclass JNICALL -Java_java_lang_Class_primitiveClass(Thread* t, jclass, jchar name) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_primitiveClass +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + char name = arguments[0]; switch (name) { case 'B': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JbyteType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JbyteType)); case 'C': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JcharType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JcharType)); case 'D': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JdoubleType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JdoubleType)); case 'F': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JfloatType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JfloatType)); case 'I': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JintType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JintType)); case 'J': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JlongType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JlongType)); case 'S': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JshortType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JshortType)); case 'V': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JvoidType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JvoidType)); case 'Z': - return makeLocalReference(t, arrayBody(t, t->m->types, Machine::JbooleanType)); + return reinterpret_cast + (arrayBody(t, t->m->types, Machine::JbooleanType)); default: t->exception = makeIllegalArgumentException(t); return 0; @@ -227,93 +273,102 @@ Java_java_lang_Class_primitiveClass(Thread* t, jclass, jchar name) } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Class_initialize(Thread* t, jobject this_) +Avian_java_lang_Class_initialize +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - initClass(t, *this_); + initClass(t, this_); } -extern "C" JNIEXPORT jboolean JNICALL -Java_java_lang_Class_isAssignableFrom(Thread* t, jobject this_, jclass that) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_isAssignableFrom +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); + object that = reinterpret_cast(arguments[1]); if (LIKELY(that)) { - return vm::isAssignableFrom(t, *this_, *that); + return vm::isAssignableFrom(t, this_, that); } else { t->exception = makeNullPointerException(t); return 0; } } -extern "C" JNIEXPORT jlong JNICALL -Java_java_lang_reflect_Field_getPrimitive -(Thread* t, jclass, jobject instance, jint code, jint offset) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getPrimitive +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object instance = reinterpret_cast(arguments[0]); + int code = arguments[1]; + int offset = arguments[2]; switch (code) { case ByteField: - return cast(*instance, offset); + return cast(instance, offset); case BooleanField: - return cast(*instance, offset); + return cast(instance, offset); case CharField: - return cast(*instance, offset); + return cast(instance, offset); case ShortField: - return cast(*instance, offset); + return cast(instance, offset); case IntField: - return cast(*instance, offset); + return cast(instance, offset); case LongField: - return cast(*instance, offset); + return cast(instance, offset); case FloatField: - return cast(*instance, offset); + return cast(instance, offset); case DoubleField: - return cast(*instance, offset); + return cast(instance, offset); default: abort(t); } } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_reflect_Field_getObject -(Thread* t, jclass, jobject instance, jint offset) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getObject +(Thread*, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object instance = reinterpret_cast(arguments[0]); + int offset = arguments[1]; - return makeLocalReference(t, cast(*instance, offset)); + return reinterpret_cast(cast(instance, offset)); } extern "C" JNIEXPORT void JNICALL -Java_java_lang_reflect_Field_setPrimitive -(Thread* t, jclass, jobject instance, jint code, jint offset, jlong value) +Avian_java_lang_reflect_Field_setPrimitive +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object instance = reinterpret_cast(arguments[0]); + int code = arguments[1]; + int offset = arguments[2]; + int64_t value; memcpy(&value, arguments + 3, 8); switch (code) { case ByteField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case BooleanField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case CharField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case ShortField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case IntField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case LongField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case FloatField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; case DoubleField: - cast(*instance, offset) = static_cast(value); + cast(instance, offset) = static_cast(value); break; default: abort(t); @@ -321,27 +376,29 @@ Java_java_lang_reflect_Field_setPrimitive } extern "C" JNIEXPORT void JNICALL -Java_java_lang_reflect_Field_setObject -(Thread* t, jclass, jobject instance, jint offset, jobject value) +Avian_java_lang_reflect_Field_setObject +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object instance = reinterpret_cast(arguments[0]); + int offset = arguments[1]; + object value = reinterpret_cast(arguments[2]); - set(t, *instance, offset, (value ? *value : 0)); + set(t, instance, offset, value); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_reflect_Constructor_make(Thread* t, jclass, jclass c) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Constructor_make +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object c = reinterpret_cast(arguments[0]); - return makeLocalReference(t, make(t, *c)); + return reinterpret_cast(make(t, c)); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_reflect_Method_getCaller(Thread* t, jclass) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_getCaller +(Thread* t, object, uintptr_t*) { - ENTER(t, Thread::ActiveState); - class Visitor: public Processor::StackVisitor { public: Visitor(Thread* t): t(t), method(0), count(0) { } @@ -363,34 +420,35 @@ Java_java_lang_reflect_Method_getCaller(Thread* t, jclass) t->m->processor->walkStack(t, &v); - return makeLocalReference(t, v.method); + return reinterpret_cast(v.method); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_reflect_Method_invoke -(Thread* t, jclass, jobject method, jobject instance, jobjectArray arguments) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_invoke +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object method = reinterpret_cast(arguments[0]); + object instance = reinterpret_cast(arguments[1]); + object args = reinterpret_cast(arguments[2]); - object v = t->m->processor->invokeArray - (t, *method, (instance ? *instance : 0), *arguments); + object v = t->m->processor->invokeArray(t, method, instance, args); if (t->exception) { t->exception = makeInvocationTargetException(t, t->exception); } - return makeLocalReference(t, v); + return reinterpret_cast(v); } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_reflect_Array_getLength(Thread* t, jclass, jobject array) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Array_getLength +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object array = reinterpret_cast(arguments[0]); if (LIKELY(array)) { - object a = *array; - unsigned elementSize = classArrayElementSize(t, objectClass(t, a)); + unsigned elementSize = classArrayElementSize(t, objectClass(t, array)); if (LIKELY(elementSize)) { - return cast(a, BytesPerWord); + return cast(array, BytesPerWord); } else { t->exception = makeIllegalArgumentException(t); } @@ -400,101 +458,113 @@ Java_java_lang_reflect_Array_getLength(Thread* t, jclass, jobject array) return 0; } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_reflect_Array_makeObjectArray -(Thread* t, jclass, jclass elementType, jint length) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Array_makeObjectArray +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object elementType = reinterpret_cast(arguments[0]); + int length = arguments[1]; - return makeLocalReference(t, makeObjectArray(t, *elementType, length)); + return reinterpret_cast(makeObjectArray(t, elementType, length)); } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_Float_floatToRawIntBits(Thread*, jclass, jfloat v) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Float_floatToRawIntBits +(Thread*, object, uintptr_t* arguments) { - return floatToBits(v); + return static_cast(*arguments); } -extern "C" JNIEXPORT jfloat JNICALL -Java_java_lang_Float_intBitsToFloat(Thread*, jclass, jint v) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Float_intBitsToFloat +(Thread*, object, uintptr_t* arguments) { - return bitsToFloat(v); + return static_cast(*arguments); } -extern "C" JNIEXPORT jlong JNICALL -Java_java_lang_Double_doubleToRawLongBits(Thread*, jclass, jdouble v) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Double_doubleToRawLongBits +(Thread*, object, uintptr_t* arguments) { - return doubleToBits(v); + int64_t v; memcpy(&v, arguments, 8); + return v; } -extern "C" JNIEXPORT jdouble JNICALL -Java_java_lang_Double_longBitsToDouble(Thread*, jclass, jlong v) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Double_longBitsToDouble +(Thread*, object, uintptr_t* arguments) { - return bitsToDouble(v); + int64_t v; memcpy(&v, arguments, 8); + return v; } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_String_intern(Thread* t, jobject this_) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_intern +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(arguments[0]); - return makeLocalReference(t, intern(t, *this_)); + return reinterpret_cast(intern(t, this_)); } -extern "C" JNIEXPORT jstring JNICALL -Java_java_lang_System_getVMProperty(Thread* t, jclass, jstring name, - jbooleanArray found) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_getVMProperty +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object name = reinterpret_cast(arguments[0]); + object found = reinterpret_cast(arguments[1]); + PROTECT(t, found); - unsigned length = stringLength(t, *name); + unsigned length = stringLength(t, name); char n[length + 1]; - stringChars(t, *name, n); + stringChars(t, name, n); - jstring r = 0; + int64_t r = 0; if (strcmp(n, "java.lang.classpath") == 0) { - r = makeLocalReference(t, makeString(t, "%s", t->m->finder->path())); + r = reinterpret_cast(makeString(t, "%s", t->m->finder->path())); } else if (strcmp(n, "avian.version") == 0) { - r = makeLocalReference(t, makeString(t, AVIAN_VERSION)); + r = reinterpret_cast(makeString(t, AVIAN_VERSION)); } else if (strcmp(n, "file.encoding") == 0) { - r = makeLocalReference(t, makeString(t, "ASCII")); + r = reinterpret_cast(makeString(t, "ASCII")); } else { const char* v = findProperty(t, n); if (v) { - r = makeLocalReference(t, makeString(t, v)); + r = reinterpret_cast(makeString(t, v)); } } if (r) { - booleanArrayBody(t, *found, 0) = true; + booleanArrayBody(t, found, 0) = true; } return r; } extern "C" JNIEXPORT void JNICALL -Java_java_lang_System_arraycopy -(Thread* t, jclass, jobject src, jint srcOffset, - jobject dst, jint dstOffset, jint length) +Avian_java_lang_System_arraycopy +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object src = reinterpret_cast(arguments[0]); + int32_t srcOffset = arguments[1]; + object dst = reinterpret_cast(arguments[2]); + int32_t dstOffset = arguments[3]; + int32_t length = arguments[4]; if (LIKELY(src and dst)) { - object s = *src; - object d = *dst; - - if (LIKELY(objectClass(t, s) == objectClass(t, d))) { - unsigned elementSize = classArrayElementSize(t, objectClass(t, s)); + if (LIKELY(compatibleArrayTypes + (t, objectClass(t, src), objectClass(t, dst)))) + { + unsigned elementSize = classArrayElementSize(t, objectClass(t, src)); if (LIKELY(elementSize)) { - intptr_t sl = cast(s, BytesPerWord); - intptr_t dl = cast(d, BytesPerWord); + intptr_t sl = cast(src, BytesPerWord); + intptr_t dl = cast(dst, BytesPerWord); if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl and dstOffset >= 0 and dstOffset + length <= dl)) { - uint8_t* sbody = &cast(s, ArrayBody); - uint8_t* dbody = &cast(d, ArrayBody); - if (s == d) { + uint8_t* sbody = &cast(src, ArrayBody); + uint8_t* dbody = &cast(dst, ArrayBody); + if (src == dst) { memmove(dbody + (dstOffset * elementSize), sbody + (srcOffset * elementSize), length * elementSize); @@ -504,8 +574,8 @@ Java_java_lang_System_arraycopy length * elementSize); } - if (classObjectMask(t, objectClass(t, d))) { - mark(t, d, ArrayBody + (dstOffset * BytesPerWord), length); + if (classObjectMask(t, objectClass(t, dst))) { + mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length); } return; @@ -520,13 +590,14 @@ Java_java_lang_System_arraycopy t->exception = makeArrayStoreException(t); } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_System_identityHashCode(Thread* t, jclass, jobject o) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_identityHashCode +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object o = reinterpret_cast(arguments[0]); if (LIKELY(o)) { - return objectHash(t, *o); + return objectHash(t, o); } else { t->exception = makeNullPointerException(t); return 0; @@ -534,14 +605,17 @@ Java_java_lang_System_identityHashCode(Thread* t, jclass, jobject o) } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Runtime_load(Thread* t, jclass, jstring name, jboolean mapName) +Avian_java_lang_Runtime_load +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); - ACQUIRE(t, t->m->classLock); + object name = reinterpret_cast(arguments[0]); + bool mapName = arguments[1]; - unsigned length = stringLength(t, *name); + unsigned length = stringLength(t, name); char n[length + 1]; - stringChars(t, *name, n); + stringChars(t, name, n); + + ACQUIRE(t, t->m->classLock); const char* builtins = findProperty(t, "avian.builtins"); if (mapName and builtins) { @@ -551,6 +625,10 @@ Java_java_lang_Runtime_load(Thread* t, jclass, jstring name, jboolean mapName) and (s[length] == ',' or s[length] == 0)) { // library is built in to this executable + if (not t->m->triedBuiltinOnLoad) { + t->m->triedBuiltinOnLoad = true; + runOnLoadIfFound(t, t->m->libraries); + } return; } else { while (*s and *s != ',') ++ s; @@ -574,6 +652,7 @@ Java_java_lang_Runtime_load(Thread* t, jclass, jstring name, jboolean mapName) System::Library* lib; if (LIKELY(t->m->system->success(t->m->system->load(&lib, n, mapName)))) { last->setNext(lib); + runOnLoadIfFound(t, lib); } else { object message = makeString(t, "library not found: %s", n); t->exception = makeUnsatisfiedLinkError(t, message); @@ -581,7 +660,8 @@ Java_java_lang_Runtime_load(Thread* t, jclass, jstring name, jboolean mapName) } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Runtime_gc(Thread* t, jobject) +Avian_java_lang_Runtime_gc +(Thread* t, object, uintptr_t*) { collect(t, Heap::MajorCollection); } @@ -589,13 +669,14 @@ Java_java_lang_Runtime_gc(Thread* t, jobject) #ifdef AVIAN_HEAPDUMP extern "C" JNIEXPORT void JNICALL -Java_java_lang_Runtime_dumpHeap(Thread* t, jclass, jstring outputFile) +Avian_avian_Machine_dumpHeap +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object outputFile = reinterpret_cast(*arguments); - unsigned length = stringLength(t, *outputFile); + unsigned length = stringLength(t, outputFile); char n[length + 1]; - stringChars(t, *outputFile, n); + stringChars(t, outputFile, n); FILE* out = fopen(n, "wb"); if (out) { { ENTER(t, Thread::ExclusiveState); @@ -611,31 +692,33 @@ Java_java_lang_Runtime_dumpHeap(Thread* t, jclass, jstring outputFile) #endif//AVIAN_HEAPDUMP extern "C" JNIEXPORT void JNICALL -Java_java_lang_Runtime_exit(Thread* t, jobject, jint code) +Avian_java_lang_Runtime_exit +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); - - t->m->system->exit(code); + t->m->system->exit(*arguments); } -extern "C" JNIEXPORT jlong JNICALL -Java_java_lang_Runtime_freeMemory(Thread*, jobject) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Runtime_freeMemory +(Thread*, object, uintptr_t*) { // todo return 0; } -extern "C" JNIEXPORT jlong JNICALL -Java_java_lang_Runtime_totalMemory(Thread*, jobject) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Runtime_totalMemory +(Thread*, object, uintptr_t*) { // todo return 0; } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_Throwable_trace(Thread* t, jclass, jint skipCount) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Throwable_trace +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + int32_t skipCount = arguments[0]; class Visitor: public Processor::StackVisitor { public: @@ -673,15 +756,17 @@ Java_java_lang_Throwable_trace(Thread* t, jclass, jint skipCount) if (v.trace == 0) v.trace = makeArray(t, 0); - return makeLocalReference(t, v.trace); + return reinterpret_cast(v.trace); } -extern "C" JNIEXPORT jarray JNICALL -Java_java_lang_Throwable_resolveTrace(Thread* t, jclass, jobject trace) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Throwable_resolveTrace +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object trace = reinterpret_cast(*arguments); + PROTECT(t, trace); - unsigned length = arrayLength(t, *trace); + unsigned length = arrayLength(t, trace); object array = makeObjectArray (t, arrayBody(t, t->m->types, Machine::StackTraceElementType), length); PROTECT(t, array); @@ -693,7 +778,7 @@ Java_java_lang_Throwable_resolveTrace(Thread* t, jclass, jobject trace) PROTECT(t, class_); for (unsigned i = 0; i < length; ++i) { - e = arrayBody(t, *trace, i); + e = arrayBody(t, trace, i); class_ = className(t, methodClass(t, traceElementMethod(t, e))); class_ = makeString(t, class_, 0, byteArrayLength(t, class_) - 1, 0); @@ -708,26 +793,26 @@ Java_java_lang_Throwable_resolveTrace(Thread* t, jclass, jobject trace) set(t, array, ArrayBody + (i * BytesPerWord), ste); } - return makeLocalReference(t, array); + return reinterpret_cast(array); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_Thread_currentThread(Thread* t, jclass) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_currentThread +(Thread* t, object, uintptr_t*) { - ENTER(t, Thread::ActiveState); - - return makeLocalReference(t, t->javaThread); + return reinterpret_cast(t->javaThread); } -extern "C" JNIEXPORT jlong JNICALL -Java_java_lang_Thread_doStart(Thread* t, jobject this_) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_doStart +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object this_ = reinterpret_cast(*arguments); - Thread* p = t->m->processor->makeThread(t->m, *this_, t); + Thread* p = t->m->processor->makeThread(t->m, this_, t); if (t->m->system->success(t->m->system->start(&(p->runnable)))) { - return reinterpret_cast(p); + return reinterpret_cast(p); } else { p->exit(); return 0; @@ -735,52 +820,58 @@ Java_java_lang_Thread_doStart(Thread* t, jobject this_) } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Thread_interrupt(Thread* t, jclass, jlong peer) +Avian_java_lang_Thread_interrupt +(Thread* t, object, uintptr_t* arguments) { + int64_t peer; memcpy(&peer, arguments, 8); + interrupt(t, reinterpret_cast(peer)); } -extern "C" JNIEXPORT jobject JNICALL -Java_java_lang_Thread_getStackTrace(Thread* t, jclass, jlong peer) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_getStackTrace +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + int64_t peer; memcpy(&peer, arguments, 8); if (reinterpret_cast(peer) == t) { - return makeLocalReference(t, makeTrace(t)); + return reinterpret_cast(makeTrace(t)); } else { - return makeLocalReference - (t, t->m->processor->getStackTrace(t, reinterpret_cast(peer))); + return reinterpret_cast + (t->m->processor->getStackTrace(t, reinterpret_cast(peer))); } } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_Thread_activeCount(Thread* t, jclass) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_activeCount +(Thread* t, object, uintptr_t*) { return t->m->liveCount; } -extern "C" JNIEXPORT jint JNICALL -Java_java_lang_Thread_enumerate(Thread* t, jclass, jobjectArray array) +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_enumerate +(Thread* t, object, uintptr_t* arguments) { + object array = reinterpret_cast(*arguments); + ACQUIRE_RAW(t, t->m->stateLock); - ENTER(t, Thread::ActiveState); - - unsigned count = min(t->m->liveCount, objectArrayLength(t, *array)); + unsigned count = min(t->m->liveCount, objectArrayLength(t, array)); unsigned index = 0; - enumerateThreads(t, t->m->rootThread, *array, &index, count); + enumerateThreads(t, t->m->rootThread, array, &index, count); return count; } -extern "C" JNIEXPORT jint JNICALL -Java_java_net_URL_00024ResourceInputStream_getContentLength -(Thread* t, jclass, jstring path) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object path = reinterpret_cast(*arguments); if (LIKELY(path)) { - char p[stringLength(t, *path) + 1]; - stringChars(t, *path, p); + char p[stringLength(t, path) + 1]; + stringChars(t, path, p); System::Region* r = t->m->finder->find(p); if (r) { @@ -792,27 +883,30 @@ Java_java_net_URL_00024ResourceInputStream_getContentLength return -1; } -extern "C" JNIEXPORT jlong JNICALL -Java_java_net_URL_00024ResourceInputStream_open -(Thread* t, jclass, jstring path) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_resource_Handler_00024ResourceInputStream_open +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + object path = reinterpret_cast(*arguments); if (LIKELY(path)) { - char p[stringLength(t, *path) + 1]; - stringChars(t, *path, p); + char p[stringLength(t, path) + 1]; + stringChars(t, path, p); - return reinterpret_cast(t->m->finder->find(p)); + return reinterpret_cast(t->m->finder->find(p)); } else { t->exception = makeNullPointerException(t); return 0; } } -extern "C" JNIEXPORT jint JNICALL -Java_java_net_URL_00024ResourceInputStream_read__JI -(Thread*, jclass, jlong peer, jint position) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_resource_Handler_00024ResourceInputStream_read__JI +(Thread*, object, uintptr_t* arguments) { + int64_t peer; memcpy(&peer, arguments, 8); + int32_t position = arguments[2]; + System::Region* region = reinterpret_cast(peer); if (position >= static_cast(region->length())) { return -1; @@ -821,12 +915,15 @@ Java_java_net_URL_00024ResourceInputStream_read__JI } } -extern "C" JNIEXPORT jint JNICALL -Java_java_net_URL_00024ResourceInputStream_read__JI_3BII -(Thread* t, jclass, jlong peer, jint position, - jbyteArray b, jint offset, jint length) +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_resource_Handler_00024ResourceInputStream_read__JI_3BII +(Thread* t, object, uintptr_t* arguments) { - ENTER(t, Thread::ActiveState); + int64_t peer; memcpy(&peer, arguments, 8); + int32_t position = arguments[2]; + object buffer = reinterpret_cast(arguments[3]); + int32_t offset = arguments[4]; + int32_t length = arguments[5]; if (length == 0) return 0; @@ -837,13 +934,60 @@ Java_java_net_URL_00024ResourceInputStream_read__JI_3BII if (length <= 0) { return -1; } else { - memcpy(&byteArrayBody(t, *b, offset), region->start() + position, length); + memcpy(&byteArrayBody(t, buffer, offset), region->start() + position, + length); return length; } } extern "C" JNIEXPORT void JNICALL -Java_java_net_URL_00024ResourceInputStream_close(Thread*, jclass, jlong peer) +Avian_avian_resource_Handler_00024ResourceInputStream_close +(Thread*, object, uintptr_t* arguments) { + int64_t peer; memcpy(&peer, arguments, 8); reinterpret_cast(peer)->dispose(); } + +extern "C" JNIEXPORT void JNICALL +Avian_avian_Continuations_callWithCurrentContinuation +(Thread* t, object, uintptr_t* arguments) +{ + t->m->processor->callWithCurrentContinuation + (t, reinterpret_cast(*arguments)); + + abort(t); +} + +extern "C" JNIEXPORT void JNICALL +Avian_avian_Continuations_dynamicWind2 +(Thread* t, object, uintptr_t* arguments) +{ + t->m->processor->dynamicWind + (t, reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1]), + reinterpret_cast(arguments[2])); + + abort(t); +} + +extern "C" JNIEXPORT void JNICALL +Avian_avian_Continuations_00024Continuation_handleResult +(Thread* t, object, uintptr_t* arguments) +{ + t->m->processor->feedResultToContinuation + (t, reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1])); + + abort(t); +} + +extern "C" JNIEXPORT void JNICALL +Avian_avian_Continuations_00024Continuation_handleException +(Thread* t, object, uintptr_t* arguments) +{ + t->m->processor->feedExceptionToContinuation + (t, reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1])); + + abort(t); +} diff --git a/src/common.h b/src/common.h index 90f3cb3241..c9964b7b7d 100644 --- a/src/common.h +++ b/src/common.h @@ -30,7 +30,7 @@ #endif #if (defined __i386__) || (defined __POWERPC__) -# define LD "d" +# define LD "ld" # define LLD "lld" #ifdef __APPLE__ # define ULD "lu" @@ -39,16 +39,18 @@ # define LX "x" # define ULD "u" #endif -#elif defined __x86_64__ && defined __LINUX__ -# define LD "ld" -# define LX "lx" -# define LLD "ld" -# define ULD "lu" -#elif defined __x86_64__ && defined __WINDOWS__ -# define LD "I64d" -# define LX "I64x" -# define LLD "I64d" -# define ULD "I64x" +#elif defined __x86_64__ +# ifdef __MINGW32__ +# define LD "I64d" +# define LX "I64x" +# define LLD "I64d" +# define ULD "I64x" +# else +# define LD "ld" +# define LX "lx" +# define LLD "ld" +# define ULD "lu" +# endif #else # error "Unsupported architecture" #endif @@ -108,10 +110,16 @@ avg(unsigned a, unsigned b) return (a + b) / 2; } +inline unsigned +pad(unsigned n, unsigned alignment) +{ + return (n + (alignment - 1)) & ~(alignment - 1); +} + inline unsigned pad(unsigned n) { - return (n + (BytesPerWord - 1)) & ~(BytesPerWord - 1); + return pad(n, BytesPerWord); } inline unsigned @@ -286,7 +294,7 @@ bitsToFloat(uint32_t bits) return f; } -inline intptr_t +inline int difference(void* a, void* b) { return reinterpret_cast(a) - reinterpret_cast(b); diff --git a/src/compile-powerpc.S b/src/compile-powerpc.S index 3a72334fcf..03e85b1ff9 100644 --- a/src/compile-powerpc.S +++ b/src/compile-powerpc.S @@ -17,14 +17,28 @@ #define ARGUMENT_BASE BYTES_PER_WORD * LINKAGE_AREA #define LOCAL(x) L##x - + #ifdef __APPLE__ -.globl _vmInvoke -_vmInvoke: +# define GLOBAL(x) _##x #else -.globl vmInvoke -vmInvoke: +# define GLOBAL(x) x #endif + +#define THREAD_CONTINUATION 96 +#define THREAD_EXCEPTION 36 +#define THREAD_EXCEPTION_STACK_ADJUSTMENT 100 +#define THREAD_EXCEPTION_OFFSET 104 +#define THREAD_EXCEPTION_HANDLER 108 + +#define CONTINUATION_NEXT 4 +#define CONTINUATION_ADDRESS 16 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 +#define CONTINUATION_FRAME_POINTER_OFFSET 24 +#define CONTINUATION_LENGTH 28 +#define CONTINUATION_BODY 32 + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): // save return address mflr r0 stw r0,8(r1) @@ -73,24 +87,94 @@ vmInvoke: // copy arguments into place li r16,0 - b LOCAL(test) + addi r18,r1,ARGUMENT_BASE + b LOCAL(vmInvoke_argumentTest) -LOCAL(loop): +LOCAL(vmInvoke_argumentLoop): lwzx r17,r16,r5 - addi r18,r16,ARGUMENT_BASE - stwx r17,r18,r1 + stwx r17,r16,r18 addi r16,r16,BYTES_PER_WORD -LOCAL(test): +LOCAL(vmInvoke_argumentTest): cmplw r16,r6 - blt LOCAL(loop) + blt LOCAL(vmInvoke_argumentLoop) // load and call function address mtctr r4 bctrl - + +LOCAL(vmInvoke_returnAddress): // restore stack pointer lwz r1,0(r1) + +#ifdef AVIAN_CONTINUATIONS + // call the next continuation, if any + lwz r5,THREAD_CONTINUATION(r13) + cmplwi r5,0 + beq LOCAL(vmInvoke_exit) + + lwz r6,CONTINUATION_LENGTH(r5) + slwi r6,r6,2 + subfic r7,r6,-80 + stwux r1,r1,r7 + + addi r7,r5,CONTINUATION_BODY + + li r8,0 + addi r10,r1,ARGUMENT_BASE + b LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + lwzx r9,r7,r8 + stwx r9,r10,r8 + addi r8,r8,4 + +LOCAL(vmInvoke_continuationTest): + cmplw r8,r6 + ble LOCAL(vmInvoke_continuationLoop) + + lwz r7,CONTINUATION_RETURN_ADDRESS_OFFSET(r5) + bl LOCAL(vmInvoke_getPC) + +LOCAL(vmInvoke_getPC): + mflr r10 + la r10,lo16(LOCAL(vmInvoke_returnAddress)-LOCAL(vmInvoke_getPC))(r10) + stwx r10,r1,r7 + + lwz r7,CONTINUATION_FRAME_POINTER_OFFSET(r5) + lwz r8,0(r1) + add r7,r7,r1 + stw r8,0(r7) + stw r7,0(r1) + + lwz r7,CONTINUATION_NEXT(r5) + stw r7,THREAD_CONTINUATION(r13) + + // call the continuation unless we're handling an exception + lwz r7,THREAD_EXCEPTION(r13) + cmpwi r7,0 + bne LOCAL(vmInvoke_handleException) + lwz r7,CONTINUATION_ADDRESS(r5) + mtctr r7 + bctr + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + li r8,0 + stw r8,THREAD_EXCEPTION(r13) + lwz r8,THREAD_EXCEPTION_STACK_ADJUSTMENT(r13) + lwz r9,0(r1) + subfic r8,r8,0 + stwux r9,r1,r8 + lwz r8,THREAD_EXCEPTION_OFFSET(r13) + stwx r7,r1,r8 + + lwz r7,THREAD_EXCEPTION_HANDLER(r13) + mtctr r7 + bctr + +LOCAL(vmInvoke_exit): +#endif // AVIAN_CONTINUATIONS // restore callee-saved registers subi r9,r1,80 @@ -118,23 +202,82 @@ LOCAL(test): // handle return value based on expected type lwz r8,44(r1) -LOCAL(void): +LOCAL(vmInvoke_void): cmplwi r8,VOID_TYPE - bne LOCAL(int64) - b LOCAL(exit) + bne LOCAL(vmInvoke_int64) + b LOCAL(vmInvoke_return) -LOCAL(int64): +LOCAL(vmInvoke_int64): cmplwi r8,INT64_TYPE - bne LOCAL(int32) - b LOCAL(exit) + bne LOCAL(vmInvoke_int32) + b LOCAL(vmInvoke_return) -LOCAL(int32): +LOCAL(vmInvoke_int32): li r3,0 -LOCAL(exit): +LOCAL(vmInvoke_return): // load return address lwz r0,8(r1) mtlr r0 // return blr + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // r3: thread + // r4: address + // r5: (unused) + // r6: stack + // r7: argumentFootprint + // r8: arguments + // r9: frameSize + + // restore (pseudo)-stack pointer (we don't want to touch the real + // stack pointer, since we haven't copied the arguments yet) + lwz r6,0(r6) + + // make everything between r1 and r6 one big stack frame while we + // shuffle things around + stw r6,0(r1) + + // allocate new frame, adding room for callee-saved registers + subfic r10,r9,-80 + stwux r6,r6,r10 + + mr r13,r3 + + // copy arguments into place + li r9,0 + addi r11,r6,ARGUMENT_BASE + b LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + lwzx r12,r8,r9 + stwx r12,r11,r9 + addi r9,r9,4 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmplw r9,r7 + ble LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + mr r1,r6 + + // set return address to vmInvoke_returnAddress + bl LOCAL(vmJumpAndInvoke_getPC) + +LOCAL(vmJumpAndInvoke_getPC): + mflr r10 + la r10,lo16(LOCAL(vmInvoke_returnAddress)-LOCAL(vmJumpAndInvoke_getPC))(r10) + mtlr r10 + + mtctr r4 + bctr +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + trap +#endif // not AVIAN_CONTINUATIONS diff --git a/src/compile-x86.S b/src/compile-x86.S index d72a4cecf0..5c73e407dc 100644 --- a/src/compile-x86.S +++ b/src/compile-x86.S @@ -12,23 +12,25 @@ #define LOCAL(x) .L##x +#if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif + .text #ifdef __x86_64__ -#ifdef __WINDOWS__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmInvoke -_vmInvoke: -# else -.globl vmInvoke -vmInvoke: -#error other -# endif +#ifdef __MINGW32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 64 + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): pushq %rbp movq %rsp,%rbp - //NEW // %rcx: thread // %rdx: function // %r8 : arguments @@ -39,7 +41,7 @@ vmInvoke: // allocate stack space, adding room for callee-saved registers movl 48(%rbp),%eax subq %rax,%rsp - subq $64,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp // save callee-saved registers movq %rsp,%r11 @@ -58,26 +60,33 @@ vmInvoke: // copy arguments into place movq $0,%r11 - jmp LOCAL(test) + jmp LOCAL(vmInvoke_argumentTest) -LOCAL(loop): +LOCAL(vmInvoke_argumentLoop): movq (%r8,%r11,1),%rsi movq %rsi,(%rsp,%r11,1) addq $8,%r11 -LOCAL(test): +LOCAL(vmInvoke_argumentTest): cmpq %r9,%r11 - jb LOCAL(loop) + jb LOCAL(vmInvoke_argumentLoop) // call function call *%rdx - + +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): // restore stack pointer movq %rbp,%rsp - - // restore callee-saved registers + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS + + // restore callee-saved registers (below the stack pointer, but in + // the red zone) movq %rsp,%r11 - subq $64,%r11 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r11 movq 0(%r11),%rbx movq 8(%r11),%r12 @@ -90,16 +99,67 @@ LOCAL(test): // return popq %rbp ret - -#elif defined __LINUX__ + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rcx: thread + // %rdx: address + // %r8 : base + // %r9 : (unused) + // 8(%rsp): argumentFootprint + // 16(%rsp): arguments + // 24(%rsp): frameSize -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmInvoke -_vmInvoke: -# else -.globl vmInvoke -vmInvoke: -# endif + movq %r8,%rbp + + // restore (pseudo)-stack pointer (we don't want to touch the real + // stack pointer, since we haven't copied the arguments yet) + movq %rbp,%r9 + + // allocate new frame, adding room for callee-saved registers + movl 24(%rsp),%eax + subq %rax,%r9 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r9 + + movq %rcx,%rbx + + // set return address + movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 + movq %r10,(%r9) + + // copy arguments into place + movq $0,%r11 + movq 16(%rsp),%r8 + movq 8(%rsp),%rax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%r8,%r11,1),%r10 + movq %r10,8(%r9,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %rax,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %r9,%rsp + + jmp *%rdx +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#else // not __MINGW32__ + +#define CALLEE_SAVED_REGISTER_FOOTPRINT 48 + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): pushq %rbp movq %rsp,%rbp @@ -111,8 +171,8 @@ vmInvoke: // %r9 : returnType (ignored) // allocate stack space, adding room for callee-saved registers - subq %r8,%rsp - subq $48,%rsp + subq %r8,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp // save callee-saved registers movq %rsp,%r9 @@ -129,26 +189,33 @@ vmInvoke: // copy arguments into place movq $0,%r9 - jmp LOCAL(test) + jmp LOCAL(vmInvoke_argumentTest) -LOCAL(loop): +LOCAL(vmInvoke_argumentLoop): movq (%rdx,%r9,1),%r8 movq %r8,(%rsp,%r9,1) addq $8,%r9 -LOCAL(test): +LOCAL(vmInvoke_argumentTest): cmpq %rcx,%r9 - jb LOCAL(loop) + jb LOCAL(vmInvoke_argumentLoop) // call function call *%rsi +.globl GLOBAL(vmInvoke_returnAddress) +GLOBAL(vmInvoke_returnAddress): // restore stack pointer movq %rbp,%rsp + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS - // restore callee-saved registers + // restore callee-saved registers (below the stack pointer, but in + // the red zone) movq %rsp,%r9 - subq $48,%r9 + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%r9 movq 0(%r9),%rbx movq 8(%r9),%r12 @@ -160,19 +227,66 @@ LOCAL(test): popq %rbp ret -#else -#error unsupported platorm / architecture combo -#endif //defined __WINDOWS__ +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // %rdi: thread + // %rsi: address + // %rdx: base + // %rcx: (unused) + // %r8 : argumentFootprint + // %r9 : arguments + // 8(%rsp): frameSize + movq %rdx,%rbp + + // restore (pseudo)-stack pointer (we don't want to touch the real + // stack pointer, since we haven't copied the arguments yet) + movq %rbp,%rcx + + // allocate new frame, adding room for callee-saved registers + movl 8(%rsp),%eax + subq %rax,%rcx + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rcx + + movq %rdi,%rbx + + // set return address + movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 + movq %r10,(%rcx) + + // copy arguments into place + movq $0,%r11 + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movq (%r9,%r11,1),%r10 + movq %r10,8(%rcx,%r11,1) + addq $8,%r11 + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpq %r8,%r11 + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + // the arguments have been copied, so we can set the real stack + // pointer now + movq %rcx,%rsp + + jmp *%rsi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // not AVIAN_CONTINUATIONS + +#endif // not __MINGW32__ + #elif defined __i386__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmInvoke -_vmInvoke: -# else -.globl vmInvoke -vmInvoke: -# endif +#define CALLEE_SAVED_REGISTER_FOOTPRINT 16 + +.globl GLOBAL(vmInvoke) +GLOBAL(vmInvoke): pushl %ebp movl %esp,%ebp @@ -185,7 +299,7 @@ vmInvoke: // allocate stack space, adding room for callee-saved registers subl 24(%ebp),%esp - subl $16,%esp + subl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp // save callee-saved registers movl %esp,%ecx @@ -201,26 +315,33 @@ vmInvoke: // copy arguments into place movl $0,%ecx movl 16(%ebp),%edx - jmp LOCAL(test) + jmp LOCAL(vmInvoke_argumentTest) -LOCAL(loop): +LOCAL(vmInvoke_argumentLoop): movl (%edx,%ecx,1),%eax movl %eax,(%esp,%ecx,1) addl $4,%ecx -LOCAL(test): +LOCAL(vmInvoke_argumentTest): cmpl 20(%ebp),%ecx - jb LOCAL(loop) + jb LOCAL(vmInvoke_argumentLoop) // call function call *12(%ebp) - - // restore stack pointer - movl %ebp,%esp + +.globl vmInvoke_returnAddress +vmInvoke_returnAddress: + // restore stack pointer, preserving the area containing saved + // registers + movl %ebp,%ecx + subl $CALLEE_SAVED_REGISTER_FOOTPRINT,%ecx + movl %ecx,%esp + +#ifdef AVIAN_CONTINUATIONS +# include "continuations-x86.S" +#endif // AVIAN_CONTINUATIONS // restore callee-saved registers - subl $16,%esp - movl 0(%esp),%ebx movl 4(%esp),%esi movl 8(%esp),%edi @@ -228,24 +349,86 @@ LOCAL(test): // handle return value based on expected type movl 28(%ebp),%ecx - addl $16,%esp + addl $CALLEE_SAVED_REGISTER_FOOTPRINT,%esp -LOCAL(void): +LOCAL(vmInvoke_void): cmpl $VOID_TYPE,%ecx - jne LOCAL(int64) - jmp LOCAL(exit) + jne LOCAL(vmInvoke_int64) + jmp LOCAL(vmInvoke_return) -LOCAL(int64): +LOCAL(vmInvoke_int64): cmpl $INT64_TYPE,%ecx - jne LOCAL(int32) - jmp LOCAL(exit) + jne LOCAL(vmInvoke_int32) + jmp LOCAL(vmInvoke_return) -LOCAL(int32): +LOCAL(vmInvoke_int32): movl $0,%edx -LOCAL(exit): +LOCAL(vmInvoke_return): popl %ebp ret + +LOCAL(getPC): + movl (%esp),%esi + ret + +.globl GLOBAL(vmJumpAndInvoke) +GLOBAL(vmJumpAndInvoke): +#ifdef AVIAN_CONTINUATIONS + // 4(%esp): thread + // 8(%esp): address + // 12(%esp): base + // 16(%esp): (unused) + // 20(%esp): argumentFootprint + // 24(%esp): arguments + // 28(%esp): frameSize + + movl 12(%esp),%ebp + + // restore (pseudo)-stack pointer (we don't want to touch the real + // stack pointer, since we haven't copied the arguments yet) + movl %ebp,%ecx + + // allocate new frame, adding room for callee-saved registers + subl 28(%esp),%ecx + subl $CALLEE_SAVED_REGISTER_FOOTPRINT,%ecx + + movl 4(%esp),%ebx + + // set return address + call LOCAL(getPC) + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl vmInvoke_returnAddress@GOT(%esi),%esi + movl %esi,(%ecx) + + // copy arguments into place + movl $0,%esi + movl 20(%esp),%edx + movl 24(%esp),%eax + jmp LOCAL(vmJumpAndInvoke_argumentTest) + +LOCAL(vmJumpAndInvoke_argumentLoop): + movl (%eax,%esi,1),%edi + movl %edi,4(%ecx,%esi,1) + addl $4,%esi + +LOCAL(vmJumpAndInvoke_argumentTest): + cmpl %edx,%esi + jb LOCAL(vmJumpAndInvoke_argumentLoop) + + movl 8(%esp),%esi + + // the arguments have been copied, so we can set the real stack + // pointer now + movl %ecx,%esp + + jmp *%esi +#else // not AVIAN_CONTINUATIONS + // vmJumpAndInvoke should only be called when continuations are + // enabled + int3 +#endif // AVIAN_CONTINUATIONS + #else #error unsupported architecture #endif //def __x86_64__ diff --git a/src/compile.cpp b/src/compile.cpp index 07d0813751..c78a1f046d 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -22,6 +22,11 @@ extern "C" uint64_t vmInvoke(void* thread, void* function, void* arguments, unsigned argumentFootprint, unsigned frameSize, unsigned returnType); +extern "C" void +vmJumpAndInvoke(void* thread, void* function, void* base, void* stack, + unsigned argumentFootprint, uintptr_t* arguments, + unsigned frameSize); + extern "C" void vmCall(); @@ -36,37 +41,52 @@ const bool DebugIntrinsics = false; const bool CheckArrayBounds = true; +#ifdef AVIAN_CONTINUATIONS +const bool Continuations = true; +#else +const bool Continuations = false; +#endif + const unsigned MaxNativeCallFootprint = 4; const unsigned InitialZoneCapacityInBytes = 64 * 1024; +const unsigned ExecutableAreaSizeInBytes = 16 * 1024 * 1024; + class MyThread: public Thread { public: class CallTrace { public: - CallTrace(MyThread* t): + CallTrace(MyThread* t, object method): t(t), base(t->base), stack(t->stack), - nativeMethod(0), + continuation(t->continuation), + nativeMethod((methodFlags(t, method) & ACC_NATIVE) ? method : 0), + targetMethod(0), + originalMethod(method), next(t->trace) { t->trace = this; t->base = 0; t->stack = 0; + t->continuation = 0; } ~CallTrace() { t->stack = stack; t->base = base; + t->continuation = continuation; t->trace = next; } MyThread* t; - void* ip; void* base; void* stack; + object continuation; object nativeMethod; + object targetMethod; + object originalMethod; CallTrace* next; }; @@ -75,6 +95,13 @@ class MyThread: public Thread { ip(0), base(0), stack(0), + continuation(0), + exceptionStackAdjustment(0), + exceptionOffset(0), + exceptionHandler(0), + tailAddress(0), + virtualCallTarget(0), + virtualCallIndex(0), trace(0), reference(0), arch(parent ? parent->arch : makeArchitecture(m->system)) @@ -85,6 +112,13 @@ class MyThread: public Thread { void* ip; void* base; void* stack; + object continuation; + uintptr_t exceptionStackAdjustment; + uintptr_t exceptionOffset; + void* exceptionHandler; + void* tailAddress; + void* virtualCallTarget; + uintptr_t virtualCallIndex; CallTrace* trace; Reference* reference; Assembler::Architecture* arch; @@ -99,15 +133,16 @@ parameterOffset(MyThread* t, object method) } object -resolveThisPointer(MyThread* t, void* stack, object method) +resolveThisPointer(MyThread* t, void* stack) { - return reinterpret_cast(stack)[parameterOffset(t, method)]; + return reinterpret_cast(stack) + [t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()]; } object resolveTarget(MyThread* t, void* stack, object method) { - object class_ = objectClass(t, resolveThisPointer(t, stack, method)); + object class_ = objectClass(t, resolveThisPointer(t, stack)); if (classVmFlags(t, class_) & BootstrapFlag) { PROTECT(t, method); @@ -124,6 +159,19 @@ resolveTarget(MyThread* t, void* stack, object method) } } +object +resolveTarget(MyThread* t, object class_, unsigned index) +{ + if (classVmFlags(t, class_) & BootstrapFlag) { + PROTECT(t, class_); + + resolveClass(t, className(t, class_)); + if (UNLIKELY(t->exception)) return 0; + } + + return arrayBody(t, classVirtualTable(t, class_), index); +} + object& methodTree(MyThread* t); @@ -178,6 +226,7 @@ class MyStackWalker: public Processor::StackWalker { enum State { Start, Next, + Continuation, Method, NativeMethod, Finish @@ -191,6 +240,7 @@ class MyStackWalker: public Processor::StackWalker { virtual void visit(Heap::Visitor* v) { v->visit(&(walker->method_)); + v->visit(&(walker->continuation)); } MyStackWalker* walker; @@ -204,6 +254,7 @@ class MyStackWalker: public Processor::StackWalker { stack(t->stack), trace(t->trace), method_(0), + continuation(t->continuation), protector(this) { } @@ -215,12 +266,13 @@ class MyStackWalker: public Processor::StackWalker { stack(w->stack), trace(w->trace), method_(w->method_), + continuation(w->continuation), protector(this) { } virtual void walk(Processor::StackVisitor* v) { for (MyStackWalker it(this); it.valid();) { - MyStackWalker walker(it); + MyStackWalker walker(&it); if (not v->visit(&walker)) { break; } @@ -250,7 +302,11 @@ class MyStackWalker: public Processor::StackWalker { method_ = methodForIp(t, ip_); if (method_) { state = Method; + } else if (continuation) { + method_ = continuationMethod(t, continuation); + state = Continuation; } else if (trace) { + continuation = trace->continuation; stack = trace->stack; base = trace->base; ip_ = t->arch->frameIp(stack); @@ -268,6 +324,7 @@ class MyStackWalker: public Processor::StackWalker { } break; + case Continuation: case Method: case NativeMethod: return true; @@ -283,6 +340,10 @@ class MyStackWalker: public Processor::StackWalker { void next() { switch (state) { + case Continuation: + continuation = continuationNext(t, continuation); + break; + case Method: t->arch->nextFrame(&stack, &base); ip_ = t->arch->frameIp(stack); @@ -307,6 +368,10 @@ class MyStackWalker: public Processor::StackWalker { virtual int ip() { switch (state) { + case Continuation: + return reinterpret_cast(continuationAddress(t, continuation)) + - methodCompiled(t, continuationMethod(t, continuation)); + case Method: return reinterpret_cast(ip_) - methodCompiled(t, method_); @@ -336,6 +401,7 @@ class MyStackWalker: public Processor::StackWalker { void* stack; MyThread::CallTrace* trace; object method_; + object continuation; MyProtector protector; }; @@ -358,19 +424,7 @@ alignedFrameSize(MyThread* t, object method) (localSize(t, method) - methodParameterFootprint(t, method) + codeMaxStack(t, methodCode(t, method)) - + t->arch->argumentFootprint(MaxNativeCallFootprint)); -} - -unsigned -usableFrameSize(MyThread* t, object method) -{ - return alignedFrameSize(t, method) - t->arch->frameFooterSize(); -} - -unsigned -usableFrameSizeWithParameters(MyThread* t, object method) -{ - return methodParameterFootprint(t, method) + usableFrameSize(t, method); + + t->arch->frameFootprint(MaxNativeCallFootprint)); } int @@ -387,19 +441,35 @@ localOffset(MyThread* t, int v, object method) - v - 1) : (frameSize + parameterFootprint - - v - 1)) * BytesPerWord; + - v - 1)); assert(t, offset >= 0); return offset; } -inline object* +int +localOffsetFromStack(MyThread* t, int index, object method) +{ + return localOffset(t, index, method) + + t->arch->frameReturnAddressSize(); +} + +object* localObject(MyThread* t, void* stack, object method, unsigned index) { - return reinterpret_cast - (static_cast(stack) - + localOffset(t, index, method) - + (t->arch->frameReturnAddressSize() * BytesPerWord)); + return static_cast(stack) + localOffsetFromStack(t, index, method); +} + +int +stackOffsetFromFrame(MyThread* t, object method) +{ + return alignedFrameSize(t, method) + t->arch->frameHeaderSize(); +} + +void* +stackForFrame(MyThread* t, void* frame, object method) +{ + return static_cast(frame) - stackOffsetFromFrame(t, method); } class PoolElement: public Promise { @@ -424,54 +494,179 @@ class PoolElement: public Promise { }; class Context; +class SubroutineCall; + +class Subroutine { + public: + Subroutine(unsigned ip, unsigned logIndex, Subroutine* listNext, + Subroutine* stackNext): + listNext(listNext), + stackNext(stackNext), + calls(0), + handle(0), + ip(ip), + logIndex(logIndex), + stackIndex(0), + callCount(0), + tableIndex(0), + visited(false) + { } + + Subroutine* listNext; + Subroutine* stackNext; + SubroutineCall* calls; + Compiler::Subroutine* handle; + unsigned ip; + unsigned logIndex; + unsigned stackIndex; + unsigned callCount; + unsigned tableIndex; + bool visited; +}; + +class SubroutinePath; + +class SubroutineCall { + public: + SubroutineCall(Subroutine* subroutine, Promise* returnAddress): + subroutine(subroutine), + returnAddress(returnAddress), + paths(0), + next(subroutine->calls) + { + subroutine->calls = this; + ++ subroutine->callCount; + } + + Subroutine* subroutine; + Promise* returnAddress; + SubroutinePath* paths; + SubroutineCall* next; +}; + +class SubroutinePath { + public: + SubroutinePath(SubroutineCall* call, SubroutinePath* stackNext, + uintptr_t* rootTable): + call(call), + stackNext(stackNext), + listNext(call->paths), + rootTable(rootTable) + { + call->paths = this; + } + + SubroutineCall* call; + SubroutinePath* stackNext; + SubroutinePath* listNext; + uintptr_t* rootTable; +}; + +void +print(SubroutinePath* path) +{ + if (path) { + fprintf(stderr, " ("); + while (true) { + fprintf(stderr, "%p", reinterpret_cast + (path->call->returnAddress->value())); + path = path->stackNext; + if (path) { + fprintf(stderr, ", "); + } else { + break; + } + } + fprintf(stderr, ")"); + } +} + +class SubroutineTrace { + public: + SubroutineTrace(SubroutinePath* path, SubroutineTrace* next): + path(path), + next(next) + { } + + SubroutinePath* path; + SubroutineTrace* next; + uintptr_t map[0]; +}; class TraceElement: public TraceHandler { public: - TraceElement(Context* context, object target, - bool virtualCall, TraceElement* next): + static const unsigned VirtualCall = 1 << 0; + static const unsigned TailCall = 1 << 1; + + TraceElement(Context* context, object target, unsigned flags, + TraceElement* next): context(context), address(0), next(next), + subroutineTrace(0), + subroutineTraceCount(0), target(target), - padIndex(0), - padding(0), - virtualCall(virtualCall) + argumentIndex(0), + flags(flags) { } - virtual void handleTrace(Promise* address, unsigned padIndex, - unsigned padding) - { + virtual void handleTrace(Promise* address, unsigned argumentIndex) { if (this->address == 0) { this->address = address; - this->padIndex = padIndex; - this->padding = padding; + this->argumentIndex = argumentIndex; } } Context* context; Promise* address; TraceElement* next; + SubroutineTrace* subroutineTrace; + unsigned subroutineTraceCount; object target; - unsigned padIndex; - unsigned padding; - bool virtualCall; + unsigned argumentIndex; + unsigned flags; uintptr_t map[0]; }; +class TraceElementPromise: public Promise { + public: + TraceElementPromise(System* s, TraceElement* trace): s(s), trace(trace) { } + + virtual int64_t value() { + assert(s, resolved()); + return trace->address->value(); + } + + virtual bool resolved() { + return trace->address != 0 and trace->address->resolved(); + } + + System* s; + TraceElement* trace; +}; + enum Event { PushContextEvent, PopContextEvent, IpEvent, MarkEvent, ClearEvent, - TraceEvent + PushExceptionHandlerEvent, + TraceEvent, + PushSubroutineEvent, + PopSubroutineEvent }; +unsigned +frameMapSizeInBits(MyThread* t, object method) +{ + return localSize(t, method) + codeMaxStack(t, methodCode(t, method)); +} + unsigned frameMapSizeInWords(MyThread* t, object method) { - return ceiling(usableFrameSizeWithParameters(t, method), BitsPerWord) - * BytesPerWord; + return ceiling(frameMapSizeInBits(t, method), BitsPerWord); } uint16_t* @@ -662,11 +857,14 @@ class Context { method(method), bootContext(bootContext), objectPool(0), - objectPoolCount(0), + subroutines(0), traceLog(0), - traceLogCount(0), visitTable(makeVisitTable(t, &zone, method)), rootTable(makeRootTable(t, &zone, method)), + subroutineTable(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), eventLog(t->m->system, t->m->heap, 1024), protector(this) { } @@ -680,11 +878,14 @@ class Context { method(0), bootContext(0), objectPool(0), - objectPoolCount(0), + subroutines(0), traceLog(0), - traceLogCount(0), visitTable(0), rootTable(0), + subroutineTable(0), + objectPoolCount(0), + traceLogCount(0), + dirtyRoots(false), eventLog(t->m->system, t->m->heap, 0), protector(this) { } @@ -702,16 +903,46 @@ class Context { object method; BootContext* bootContext; PoolElement* objectPool; - unsigned objectPoolCount; + Subroutine* subroutines; TraceElement* traceLog; - unsigned traceLogCount; uint16_t* visitTable; uintptr_t* rootTable; + Subroutine** subroutineTable; + unsigned objectPoolCount; + unsigned traceLogCount; bool dirtyRoots; Vector eventLog; MyProtector protector; }; +unsigned +translateLocalIndex(Context* context, unsigned footprint, unsigned index) +{ + unsigned parameterFootprint = methodParameterFootprint + (context->thread, context->method); + + if (index < parameterFootprint) { + return parameterFootprint - index - footprint; + } else { + return index; + } +} + +Compiler::Operand* +loadLocal(Context* context, unsigned footprint, unsigned index) +{ + return context->compiler->loadLocal + (footprint, translateLocalIndex(context, footprint, index)); +} + +void +storeLocal(Context* context, unsigned footprint, Compiler::Operand* value, + unsigned index) +{ + context->compiler->storeLocal + (footprint, value, translateLocalIndex(context, footprint, index)); +} + class Frame { public: enum StackType { @@ -770,7 +1001,7 @@ class Frame { object pointer = makePointer(t, p); bc->constants = makeTriple(t, o, pointer, bc->constants); - return c->promiseConstant(p); + return c->promiseConstant(p, ObjectField); } else { context->objectPool = new (context->zone.allocate(sizeof(PoolElement))) @@ -988,11 +1219,11 @@ class Frame { } Compiler::Operand* addressOperand(Promise* p) { - return c->promiseConstant(addressPromise(p)); + return c->promiseConstant(addressPromise(p), ObjectField); } Compiler::Operand* machineIp(unsigned logicalIp) { - return c->promiseConstant(c->machineIp(logicalIp)); + return c->promiseConstant(c->machineIp(logicalIp), ObjectField); } void visitLogicalIp(unsigned ip) { @@ -1003,6 +1234,10 @@ class Frame { } void startLogicalIp(unsigned ip) { + if (subroutine) { + context->subroutineTable[ip] = subroutine; + } + c->startLogicalIp(ip); context->eventLog.append(IpEvent); @@ -1057,13 +1292,7 @@ class Frame { void pop(unsigned count) { popped(count); - - for (unsigned i = count; i;) { - Compiler::StackElement* s = c->top(); - unsigned footprint = c->footprint(s); - c->popped(footprint); - i -= footprint; - } + c->popped(count); } Compiler::Operand* popInt() { @@ -1083,43 +1312,43 @@ class Frame { void loadInt(unsigned index) { assert(t, index < localSize()); - pushInt(c->loadLocal(1, index)); + pushInt(loadLocal(context, 1, index)); } void loadLong(unsigned index) { assert(t, index < static_cast(localSize() - 1)); - pushLong(c->loadLocal(2, index)); + pushLong(loadLocal(context, 2, index)); } void loadObject(unsigned index) { assert(t, index < localSize()); - pushObject(c->loadLocal(1, index)); + pushObject(loadLocal(context, 1, index)); } void storeInt(unsigned index) { - c->storeLocal(1, popInt(), index); - storedInt(index); + storeLocal(context, 1, popInt(), index); + storedInt(translateLocalIndex(context, 1, index)); } void storeLong(unsigned index) { - c->storeLocal(2, popLong(), index); - storedLong(index); + storeLocal(context, 2, popLong(), index); + storedLong(translateLocalIndex(context, 2, index)); } void storeObject(unsigned index) { - c->storeLocal(1, popObject(), index); - storedObject(index); + storeLocal(context, 1, popObject(), index); + storedObject(translateLocalIndex(context, 1, index)); } void storeObjectOrAddress(unsigned index) { - c->storeLocal(1, popQuiet(1), index); + storeLocal(context, 1, popQuiet(1), index); assert(t, sp >= 1); assert(t, sp - 1 >= localSize()); if (get(sp - 1) == Object) { - storedObject(index); + storedObject(translateLocalIndex(context, 1, index)); } else { - storedInt(index); + storedInt(translateLocalIndex(context, 1, index)); } popped(1); @@ -1249,12 +1478,12 @@ class Frame { swapped(); } - TraceElement* trace(object target, bool virtualCall) { + TraceElement* trace(object target, unsigned flags) { unsigned mapSize = frameMapSizeInWords(t, context->method); TraceElement* e = context->traceLog = new (context->zone.allocate(sizeof(TraceElement) + (mapSize * BytesPerWord))) - TraceElement(context, target, virtualCall, context->traceLog); + TraceElement(context, target, flags, context->traceLog); ++ context->traceLogCount; @@ -1263,11 +1492,76 @@ class Frame { return e; } + + unsigned startSubroutine(unsigned ip, Promise* returnAddress) { + pushAddress(addressOperand(returnAddress)); + + Subroutine* subroutine = 0; + for (Subroutine* s = context->subroutines; s; s = s->listNext) { + if (s->ip == ip) { + subroutine = s; + break; + } + } + + if (subroutine == 0) { + context->subroutines = subroutine = new + (context->zone.allocate(sizeof(Subroutine))) + Subroutine(ip, context->eventLog.length() + 1 + BytesPerWord + 2, + context->subroutines, this->subroutine); + + if (context->subroutineTable == 0) { + unsigned size = codeLength(t, methodCode(t, context->method)) + * sizeof(Subroutine*); + + context->subroutineTable = static_cast + (context->zone.allocate(size)); + + memset(context->subroutineTable, 0, size); + } + } + + subroutine->handle = c->startSubroutine(); + this->subroutine = subroutine; + + SubroutineCall* call = new + (context->zone.allocate(sizeof(SubroutineCall))) + SubroutineCall(subroutine, returnAddress); + + context->eventLog.append(PushSubroutineEvent); + context->eventLog.appendAddress(call); + + unsigned nextIndexIndex = context->eventLog.length(); + context->eventLog.append2(0); + + c->saveLocals(); + + return nextIndexIndex; + } + + void returnFromSubroutine(unsigned returnAddressLocal) { + c->endSubroutine(subroutine->handle); + subroutine->stackIndex = localOffsetFromStack + (t, translateLocalIndex(context, 1, returnAddressLocal), + context->method); + } + + void endSubroutine(unsigned nextIndexIndex) { + c->linkSubroutine(subroutine->handle); + + poppedInt(); + + context->eventLog.append(PopSubroutineEvent); + + context->eventLog.set2(nextIndexIndex, context->eventLog.length()); + + subroutine = subroutine->stackNext; + } Context* context; MyThread* t; Compiler* c; - Compiler::Subroutine* subroutine; + Subroutine* subroutine; uint8_t* stackMap; unsigned ip; unsigned sp; @@ -1289,22 +1583,25 @@ insertCallNode(MyThread* t, object node); void* findExceptionHandler(Thread* t, object method, void* ip) { - object table = codeExceptionHandlerTable(t, methodCode(t, method)); - if (table) { - object index = arrayBody(t, table, 0); + if (t->exception) { + object table = codeExceptionHandlerTable(t, methodCode(t, method)); + if (table) { + object index = arrayBody(t, table, 0); - uint8_t* compiled = reinterpret_cast(methodCompiled(t, method)); + uint8_t* compiled = reinterpret_cast + (methodCompiled(t, method)); - for (unsigned i = 0; i < arrayLength(t, table) - 1; ++i) { - unsigned start = intArrayBody(t, index, i * 3); - unsigned end = intArrayBody(t, index, (i * 3) + 1); - unsigned key = difference(ip, compiled) - 1; + for (unsigned i = 0; i < arrayLength(t, table) - 1; ++i) { + unsigned start = intArrayBody(t, index, i * 3); + unsigned end = intArrayBody(t, index, (i * 3) + 1); + unsigned key = difference(ip, compiled) - 1; - if (key >= start and key < end) { - object catchType = arrayBody(t, table, i + 1); + if (key >= start and key < end) { + object catchType = arrayBody(t, table, i + 1); - if (catchType == 0 or instanceOf(t, catchType, t->exception)) { - return compiled + intArrayBody(t, index, (i * 3) + 2); + if (catchType == 0 or instanceOf(t, catchType, t->exception)) { + return compiled + intArrayBody(t, index, (i * 3) + 2); + } } } } @@ -1313,6 +1610,23 @@ findExceptionHandler(Thread* t, object method, void* ip) return 0; } +void +releaseLock(MyThread* t, object method, void* stack) +{ + if (methodFlags(t, method) & ACC_SYNCHRONIZED) { + object lock; + if (methodFlags(t, method) & ACC_STATIC) { + lock = methodClass(t, method); + } else { + lock = *localObject + (t, stackForFrame(t, stack, method), method, + savedTargetIndex(t, method)); + } + + release(t, lock); + } +} + void findUnwindTarget(MyThread* t, void** targetIp, void** targetBase, void** targetStack) @@ -1324,50 +1638,166 @@ findUnwindTarget(MyThread* t, void** targetIp, void** targetBase, ip = t->arch->frameIp(stack); } + object target = t->trace->targetMethod; + *targetIp = 0; while (*targetIp == 0) { object method = methodForIp(t, ip); if (method) { - PROTECT(t, method); - void* handler = findExceptionHandler(t, method, ip); if (handler) { - void** sp = static_cast(stack) - + t->arch->frameReturnAddressSize(); - - sp[localOffset(t, localSize(t, method), method) / BytesPerWord] - = t->exception; - - t->exception = 0; - *targetIp = handler; *targetBase = base; - *targetStack = sp; - } else { - if (methodFlags(t, method) & ACC_SYNCHRONIZED) { - object lock; - if (methodFlags(t, method) & ACC_STATIC) { - lock = methodClass(t, method); - } else { - lock = *localObject(t, stack, method, savedTargetIndex(t, method)); - } - - release(t, lock); - } + t->arch->nextFrame(&stack, &base); + + void** sp = static_cast(stackForFrame(t, stack, method)) + + t->arch->frameReturnAddressSize(); + + *targetStack = sp; + + sp[localOffset(t, localSize(t, method), method)] = t->exception; + + t->exception = 0; + } else { t->arch->nextFrame(&stack, &base); ip = t->arch->frameIp(stack); + + if (t->exception) { + releaseLock(t, method, stack); + } + + target = method; } } else { *targetIp = ip; *targetBase = base; *targetStack = static_cast(stack) + t->arch->frameReturnAddressSize(); + + while (Continuations and t->continuation) { + object c = t->continuation; + + object method = continuationMethod(t, c); + + void* handler = findExceptionHandler + (t, method, continuationAddress(t, c)); + + if (handler) { + t->exceptionHandler = handler; + + t->exceptionStackAdjustment + = (stackOffsetFromFrame(t, method) + - ((continuationFramePointerOffset(t, c) / BytesPerWord) + - t->arch->framePointerOffset() + + t->arch->frameReturnAddressSize())) * BytesPerWord; + + t->exceptionOffset + = localOffset(t, localSize(t, method), method) * BytesPerWord; + break; + } else if (t->exception) { + releaseLock(t, method, + reinterpret_cast(c) + + ContinuationBody + + continuationReturnAddressOffset(t, c) + - t->arch->returnAddressOffset()); + } + + t->continuation = continuationNext(t, c); + } } } } +object +makeCurrentContinuation(MyThread* t, void** targetIp, void** targetBase, + void** targetStack) +{ + void* ip = t->ip; + void* base = t->base; + void* stack = t->stack; + if (ip == 0) { + ip = t->arch->frameIp(stack); + } + + object context = t->continuation + ? continuationContext(t, t->continuation) + : makeContinuationContext(t, 0, 0, 0, 0, t->trace->originalMethod); + PROTECT(t, context); + + object target = t->trace->targetMethod; + PROTECT(t, target); + + object first = 0; + PROTECT(t, first); + + object last = 0; + PROTECT(t, last); + + *targetIp = 0; + while (*targetIp == 0) { + object method = methodForIp(t, ip); + if (method) { + PROTECT(t, method); + + void** top = static_cast(stack) + + t->arch->frameReturnAddressSize() + + t->arch->frameFooterSize(); + unsigned argumentFootprint + = t->arch->argumentFootprint(methodParameterFootprint(t, target)); + unsigned alignment = t->arch->stackAlignmentInWords(); + if (TailCalls and argumentFootprint > alignment) { + top += argumentFootprint - alignment; + } + + t->arch->nextFrame(&stack, &base); + + void** bottom = static_cast(stack) + + t->arch->frameReturnAddressSize(); + unsigned frameSize = bottom - top; + unsigned totalSize = frameSize + + t->arch->frameFooterSize() + + t->arch->argumentFootprint(methodParameterFootprint(t, method)); + + object c = makeContinuation + (t, 0, context, method, ip, + ((frameSize + + t->arch->frameFooterSize() + + t->arch->returnAddressOffset() + - t->arch->frameReturnAddressSize()) * BytesPerWord), + ((frameSize + + t->arch->frameFooterSize() + + t->arch->framePointerOffset() + - t->arch->frameReturnAddressSize()) * BytesPerWord), + totalSize); + + memcpy(&continuationBody(t, c, 0), top, totalSize * BytesPerWord); + + if (last) { + set(t, last, ContinuationNext, c); + } else { + first = c; + } + last = c; + + ip = t->arch->frameIp(stack); + + target = method; + } else { + *targetIp = ip; + *targetBase = base; + *targetStack = static_cast(stack) + + t->arch->frameReturnAddressSize();; + } + } + + expect(t, last); + set(t, last, ContinuationNext, t->continuation); + + return first; +} + void NO_RETURN unwind(MyThread* t) { @@ -1375,12 +1805,21 @@ unwind(MyThread* t) void* base; void* stack; findUnwindTarget(t, &ip, &base, &stack); - vmJump(ip, base, stack, t); + vmJump(ip, base, stack, t, 0, 0); } object& objectPools(MyThread* t); +object& +windMethod(MyThread* t); + +object& +rewindMethod(MyThread* t); + +object& +receiveMethod(MyThread* t); + uintptr_t defaultThunk(MyThread* t); @@ -1390,6 +1829,9 @@ nativeThunk(MyThread* t); uintptr_t aioobThunk(MyThread* t); +uintptr_t +virtualThunk(MyThread* t, unsigned index); + uintptr_t methodAddress(Thread* t, object method) { @@ -1407,12 +1849,33 @@ tryInitClass(MyThread* t, object class_) if (UNLIKELY(t->exception)) unwind(t); } +FixedAllocator* +codeAllocator(MyThread* t); + +void +compile(MyThread* t, Allocator* allocator, BootContext* bootContext, + object method); + int64_t findInterfaceMethodFromInstance(MyThread* t, object method, object instance) { if (instance) { - return methodAddress - (t, findInterfaceMethod(t, method, objectClass(t, instance))); + object target = findInterfaceMethod(t, method, objectClass(t, instance)); + + if (methodAddress(t, target) == defaultThunk(t)) { + PROTECT(t, target); + + compile(t, codeAllocator(t), 0, target); + } + + if (UNLIKELY(t->exception)) { + unwind(t); + } else { + if (methodFlags(t, target) & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + return methodAddress(t, target); + } } else { t->exception = makeNullPointerException(t); unwind(t); @@ -1851,9 +2314,9 @@ instanceOf64(Thread* t, object class_, object o) } uint64_t -makeNewWeakReference64(Thread* t, object class_) +makeNewGeneral64(Thread* t, object class_) { - return reinterpret_cast(makeNewWeakReference(t, class_)); + return reinterpret_cast(makeNewGeneral(t, class_)); } uint64_t @@ -1922,24 +2385,76 @@ pushReturnValue(MyThread* t, Frame* frame, unsigned code, } } -bool -emptyMethod(MyThread* t, object method) -{ - return ((methodFlags(t, method) & ACC_NATIVE) == 0) - and (codeLength(t, methodCode(t, method)) == 1) - and (codeBody(t, methodCode(t, method), 0) == return_); -} - -void -compileDirectInvoke(MyThread* t, Frame* frame, object target) +Compiler::Operand* +compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall, + bool useThunk, unsigned rSize, Promise* addressPromise) { Compiler* c = frame->c; + unsigned flags = (tailCall ? Compiler::TailJump : 0); + + if (useThunk or (tailCall and (methodFlags(t, target) & ACC_NATIVE))) { + if (TailCalls and tailCall) { + TraceElement* trace = frame->trace(target, TraceElement::TailCall); + Compiler::Operand* returnAddress = c->promiseConstant + (new (frame->context->zone.allocate(sizeof(TraceElementPromise))) + TraceElementPromise(t->m->system, trace), ObjectField);; + + Compiler::Operand* result = c->stackCall + (returnAddress, + flags | Compiler::Aligned, + trace, + rSize, + methodReturnCode(t, target), + methodParameterFootprint(t, target)); + + c->store(BytesPerWord, returnAddress, BytesPerWord, + c->memory(c->register_(t->arch->thread()), + difference(&(t->tailAddress), t))); + + if (methodFlags(t, target) & ACC_NATIVE) { + c->exit(c->constant(nativeThunk(t), ObjectField)); + } else { + c->exit(c->constant(defaultThunk(t), ObjectField)); + } + + return result; + } else { + return c->stackCall + (c->constant(defaultThunk(t), ObjectField), + flags | Compiler::Aligned, + frame->trace(target, 0), + rSize, + methodReturnCode(t, target), + methodParameterFootprint(t, target)); + } + } else { + Compiler::Operand* address = + (addressPromise + ? c->promiseConstant(addressPromise, ObjectField) + : c->constant(methodAddress(t, target), ObjectField)); + + return c->stackCall + (address, + flags, + tailCall ? 0 : frame->trace + ((methodFlags(t, target) & ACC_NATIVE) ? target : 0, 0), + rSize, + methodReturnCode(t, target), + methodParameterFootprint(t, target)); + } +} + +bool +compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall) +{ unsigned rSize = resultSize(t, methodReturnCode(t, target)); Compiler::Operand* result = 0; - if (not emptyMethod(t, target)) { + if (emptyMethod(t, target)) { + tailCall = false; + } else { BootContext* bc = frame->context->bootContext; if (bc) { if (methodClass(t, target) == methodClass(t, frame->context->method) @@ -1952,42 +2467,20 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target) object pointer = makePointer(t, p); bc->calls = makeTriple(t, target, pointer, bc->calls); - object traceTarget - = (methodFlags(t, target) & ACC_NATIVE) ? target : 0; - - result = c->stackCall - (c->promiseConstant(p), - 0, - frame->trace(traceTarget, false), - rSize, - methodParameterFootprint(t, target)); + result = compileDirectInvoke + (t, frame, target, tailCall, false, rSize, p); } else { - result = c->stackCall - (c->constant(defaultThunk(t)), - Compiler::Aligned, - frame->trace(target, false), - rSize, - methodParameterFootprint(t, target)); + result = compileDirectInvoke + (t, frame, target, tailCall, true, rSize, 0); } } else if (methodAddress(t, target) == defaultThunk(t) or classNeedsInit(t, methodClass(t, target))) { - result = c->stackCall - (c->constant(defaultThunk(t)), - Compiler::Aligned, - frame->trace(target, false), - rSize, - methodParameterFootprint(t, target)); + result = compileDirectInvoke + (t, frame, target, tailCall, true, rSize, 0); } else { - object traceTarget - = (methodFlags(t, target) & ACC_NATIVE) ? target : 0; - - result = c->stackCall - (c->constant(methodAddress(t, target)), - 0, - frame->trace(traceTarget, false), - rSize, - methodParameterFootprint(t, target)); + result = compileDirectInvoke + (t, frame, target, tailCall, false, rSize, 0); } } @@ -1996,6 +2489,8 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target) if (rSize) { pushReturnValue(t, frame, methodReturnCode(t, target), result); } + + return tailCall; } void @@ -2009,14 +2504,15 @@ handleMonitorEvent(MyThread* t, Frame* frame, intptr_t function) if (methodFlags(t, method) & ACC_STATIC) { lock = frame->append(methodClass(t, method)); } else { - lock = c->loadLocal(1, savedTargetIndex(t, method)); + lock = loadLocal(frame->context, 1, savedTargetIndex(t, method)); } - c->call(c->constant(function), + c->call(c->constant(function, ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 2, c->thread(), lock); + VoidField, + 2, c->register_(t->arch->thread()), lock); } } @@ -2028,11 +2524,9 @@ handleEntrance(MyThread* t, Frame* frame) if ((methodFlags(t, method) & (ACC_SYNCHRONIZED | ACC_STATIC)) == ACC_SYNCHRONIZED) { - Compiler* c = frame->c; - // save 'this' pointer in case it is overwritten. unsigned index = savedTargetIndex(t, method); - c->storeLocal(1, c->loadLocal(1, 0), index); + storeLocal(frame->context, 1, loadLocal(frame->context, 1, 0), index); frame->set(index, Frame::Object); } @@ -2047,67 +2541,6 @@ handleExit(MyThread* t, Frame* frame) (t, frame, getThunk(t, releaseMonitorForObjectThunk)); } -int -exceptionIndex(MyThread* t, object code, unsigned jsrIp, unsigned dstIp) -{ - object table = codeExceptionHandlerTable(t, code); - unsigned length = exceptionHandlerTableLength(t, table); - for (unsigned i = 0; i < length; ++i) { - ExceptionHandler* eh = exceptionHandlerTableBody(t, table, i); - if (exceptionHandlerCatchType(eh) == 0) { - unsigned ip = exceptionHandlerIp(eh); - unsigned index; - switch (codeBody(t, code, ip++)) { - case astore: - index = codeBody(t, code, ip++); - break; - - case astore_0: - index = 0; - break; - - case astore_1: - index = 1; - break; - - case astore_2: - index = 2; - break; - - case astore_3: - index = 3; - break; - - default: abort(t); - } - - if (ip == jsrIp) { - return -1; - } - - switch (codeBody(t, code, ip++)) { - case jsr: { - uint32_t offset = codeReadInt16(t, code, ip); - if ((ip - 3) + offset == dstIp) { - return index; - } - } break; - - case jsr_w: { - uint32_t offset = codeReadInt32(t, code, ip); - if ((ip - 5) + offset == dstIp) { - return index; - } - } break; - - default: break; - } - } - } - - abort(t); -} - bool inTryBlock(MyThread* t, object code, unsigned ip) { @@ -2126,6 +2559,58 @@ inTryBlock(MyThread* t, object code, unsigned ip) return false; } +bool +needsReturnBarrier(MyThread* t, object method) +{ + return (methodFlags(t, method) & ConstructorFlag) + and (classFlags(t, methodClass(t, method)) & HasFinalMemberFlag); +} + +bool +returnsNext(MyThread* t, object code, unsigned ip) +{ + switch (codeBody(t, code, ip)) { + case return_: + case areturn: + case ireturn: + case freturn: + case lreturn: + case dreturn: + return true; + + case goto_: { + uint32_t offset = codeReadInt16(t, code, ++ip); + uint32_t newIp = (ip - 3) + offset; + assert(t, newIp < codeLength(t, code)); + + return returnsNext(t, code, newIp); + } + + case goto_w: { + uint32_t offset = codeReadInt32(t, code, ++ip); + uint32_t newIp = (ip - 5) + offset; + assert(t, newIp < codeLength(t, code)); + + return returnsNext(t, code, newIp); + } + + default: + return false; + } +} + +bool +isTailCall(MyThread* t, object code, unsigned ip, object caller, object callee) +{ + return TailCalls + and ((methodFlags(t, caller) & ACC_SYNCHRONIZED) == 0) + and (not inTryBlock(t, code, ip - 1)) + and (not needsReturnBarrier(t, caller)) + and (methodReturnCode(t, caller) == VoidField + or methodReturnCode(t, caller) == methodReturnCode(t, callee)) + and returnsNext(t, code, ip); +} + void compile(MyThread* t, Frame* initialFrame, unsigned ip, int exceptionHandlerStart = -1); @@ -2187,11 +2672,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushObject(); c->call - (c->constant(getThunk(t, gcIfNecessaryThunk)), + (c->constant(getThunk(t, gcIfNecessaryThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 1, c->thread()); + VoidField, + 1, c->register_(t->arch->thread())); } ++ lastFcmpl; @@ -2226,35 +2712,41 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushObject (c->load (BytesPerWord, BytesPerWord, - c->memory(array, ArrayBody, index, BytesPerWord), BytesPerWord)); + c->memory(array, ObjectField, ArrayBody, index, BytesPerWord), BytesPerWord)); break; case faload: + frame->pushInt + (c->load(4, 4, c->memory(array, FloatField, ArrayBody, index, 4), BytesPerWord)); + break; case iaload: frame->pushInt - (c->load(4, 4, c->memory(array, ArrayBody, index, 4), BytesPerWord)); + (c->load(4, 4, c->memory(array, IntField, ArrayBody, index, 4), BytesPerWord)); break; case baload: frame->pushInt - (c->load(1, 1, c->memory(array, ArrayBody, index, 1), BytesPerWord)); + (c->load(1, 1, c->memory(array, ByteField, ArrayBody, index, 1), BytesPerWord)); break; case caload: frame->pushInt - (c->loadz(2, 2, c->memory(array, ArrayBody, index, 2), + (c->loadz(2, 2, c->memory(array, CharField, ArrayBody, index, 2), BytesPerWord)); break; case daload: + frame->pushLong + (c->load(8, 8, c->memory(array, DoubleField, ArrayBody, index, 8), 8)); + break; case laload: frame->pushLong - (c->load(8, 8, c->memory(array, ArrayBody, index, 8), 8)); + (c->load(8, 8, c->memory(array, LongField, ArrayBody, index, 8), 8)); break; case saload: frame->pushInt - (c->load(2, 2, c->memory(array, ArrayBody, index, 2), BytesPerWord)); + (c->load(2, 2, c->memory(array, ShortField, ArrayBody, index, 2), BytesPerWord)); break; } } break; @@ -2290,42 +2782,48 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, switch (instruction) { case aastore: { c->call - (c->constant(getThunk(t, setMaybeNullThunk)), + (c->constant(getThunk(t, setMaybeNullThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 4, c->thread(), array, - c->add(4, c->constant(ArrayBody), - c->shl(4, c->constant(log(BytesPerWord)), index)), + VoidField, + 4, c->register_(t->arch->thread()), array, + c->add(4, c->constant(ArrayBody, ObjectField), + c->shl(4, c->constant(log(BytesPerWord), ObjectField), index)), value); } break; case fastore: + c->store + (BytesPerWord, value, 4, c->memory(array, FloatField, ArrayBody, index, 4)); + break; case iastore: c->store - (BytesPerWord, value, 4, c->memory(array, ArrayBody, index, 4)); + (BytesPerWord, value, 4, c->memory(array, IntField, ArrayBody, index, 4)); break; case bastore: c->store - (BytesPerWord, value, 1, c->memory(array, ArrayBody, index, 1)); + (BytesPerWord, value, 1, c->memory(array, ByteField, ArrayBody, index, 1)); break; case castore: case sastore: c->store - (BytesPerWord, value, 2, c->memory(array, ArrayBody, index, 2)); + (BytesPerWord, value, 2, c->memory(array, ShortField, ArrayBody, index, 2)); break; case dastore: + c->store(8, value, 8, c->memory(array, DoubleField, ArrayBody, index, 8)); + break; case lastore: - c->store(8, value, 8, c->memory(array, ArrayBody, index, 8)); + c->store(8, value, 8, c->memory(array, LongField, ArrayBody, index, 8)); break; } } break; case aconst_null: - frame->pushObject(c->constant(0)); + frame->pushObject(c->constant(0, ObjectField)); break; case aload: @@ -2358,11 +2856,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushObject (c->call - (c->constant(getThunk(t, makeBlankObjectArrayThunk)), + (c->constant(getThunk(t, makeBlankObjectArrayThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 3, c->thread(), frame->append(class_), length)); + ObjectField, + 3, c->register_(t->arch->thread()), frame->append(class_), length)); } break; case areturn: { @@ -2374,7 +2873,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushInt (c->load (BytesPerWord, BytesPerWord, - c->memory(frame->popObject(), ArrayLength, 0, 1), BytesPerWord)); + c->memory(frame->popObject(), IntField, ArrayLength, 0, 1), BytesPerWord)); } break; case astore: @@ -2400,16 +2899,17 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case athrow: { Compiler::Operand* target = frame->popObject(); c->call - (c->constant(getThunk(t, throw_Thunk)), + (c->constant(getThunk(t, throw_Thunk), ObjectField), Compiler::NoReturn, - frame->trace(0, false), + frame->trace(0, 0), 0, - 2, c->thread(), target); + VoidField, + 2, c->register_(t->arch->thread()), target); } return; case bipush: frame->pushInt - (c->constant(static_cast(codeBody(t, code, ip++)))); + (c->constant(static_cast(codeBody(t, code, ip++)), IntField)); break; case checkcast: { @@ -2421,11 +2921,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* instance = c->peek(1, 0); c->call - (c->constant(getThunk(t, checkCastThunk)), + (c->constant(getThunk(t, checkCastThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 3, c->thread(), frame->append(class_), instance); + VoidField, + 3, c->register_(t->arch->thread()), frame->append(class_), instance); } break; case d2f: { @@ -2457,8 +2958,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { frame->pushInt (c->call - (c->constant(getThunk(t, compareDoublesGThunk)), - 0, 0, 4, 4, + (c->constant(getThunk(t, compareDoublesGThunk), ObjectField), + 0, 0, 4, IntField, 4, static_cast(0), a, static_cast(0), b)); } @@ -2474,19 +2975,19 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { frame->pushInt (c->call - (c->constant(getThunk(t, compareDoublesLThunk)), - 0, 0, 4, 4, + (c->constant(getThunk(t, compareDoublesLThunk), ObjectField), + 0, 0, 4, IntField, 4, static_cast(0), a, static_cast(0), b)); } } break; case dconst_0: - frame->pushLong(c->constant(doubleToBits(0.0))); + frame->pushLong(c->constant(doubleToBits(0.0), DoubleField)); break; case dconst_1: - frame->pushLong(c->constant(doubleToBits(1.0))); + frame->pushLong(c->constant(doubleToBits(1.0), DoubleField)); break; case ddiv: { @@ -2574,8 +3075,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { frame->pushInt (c->call - (c->constant(getThunk(t, compareFloatsGThunk)), - 0, 0, 4, 2, a, b)); + (c->constant(getThunk(t, compareFloatsGThunk), ObjectField), + 0, 0, 4, IntField, 2, a, b)); } } break; @@ -2589,21 +3090,21 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { frame->pushInt (c->call - (c->constant(getThunk(t, compareFloatsLThunk)), - 0, 0, 4, 2, a, b)); + (c->constant(getThunk(t, compareFloatsLThunk), ObjectField), + 0, 0, 4, IntField, 2, a, b)); } } break; case fconst_0: - frame->pushInt(c->constant(floatToBits(0.0))); + frame->pushInt(c->constant(floatToBits(0.0), FloatField)); break; case fconst_1: - frame->pushInt(c->constant(floatToBits(1.0))); + frame->pushInt(c->constant(floatToBits(1.0), FloatField)); break; case fconst_2: - frame->pushInt(c->constant(floatToBits(2.0))); + frame->pushInt(c->constant(floatToBits(2.0), FloatField)); break; case fdiv: { @@ -2654,11 +3155,13 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, and classNeedsInit(t, fieldClass(t, field))) { c->call - (c->constant(getThunk(t, tryInitClassThunk)), + (c->constant(getThunk(t, tryInitClassThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 2, c->thread(), frame->append(fieldClass(t, field))); + VoidField, + 2, c->register_(t->arch->thread()), + frame->append(fieldClass(t, field))); } table = frame->append(classStaticTable(t, fieldClass(t, field))); @@ -2682,48 +3185,49 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, fieldOperand = frame->append(field); c->call - (c->constant(getThunk(t, acquireMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), fieldOperand); + (c->constant(getThunk(t, acquireMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), + fieldOperand); } switch (fieldCode(t, field)) { case ByteField: case BooleanField: frame->pushInt - (c->load(1, 1, c->memory(table, fieldOffset(t, field), 0, 1), + (c->load(1, 1, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), BytesPerWord)); break; case CharField: frame->pushInt - (c->loadz(2, 2, c->memory(table, fieldOffset(t, field), 0, 1), + (c->loadz(2, 2, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), BytesPerWord)); break; case ShortField: frame->pushInt - (c->load(2, 2, c->memory(table, fieldOffset(t, field), 0, 1), + (c->load(2, 2, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), BytesPerWord)); break; case FloatField: case IntField: frame->pushInt - (c->load(4, 4, c->memory(table, fieldOffset(t, field), 0, 1), + (c->load(4, 4, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), BytesPerWord)); break; case DoubleField: case LongField: frame->pushLong - (c->load(8, 8, c->memory(table, fieldOffset(t, field), 0, 1), 8)); + (c->load(8, 8, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), 8)); break; case ObjectField: frame->pushObject (c->load (BytesPerWord, BytesPerWord, - c->memory(table, fieldOffset(t, field), 0, 1), BytesPerWord)); + c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1), BytesPerWord)); break; default: @@ -2736,8 +3240,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, or fieldCode(t, field) == LongField)) { c->call - (c->constant(getThunk(t, releaseMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), fieldOperand); + (c->constant(getThunk(t, releaseMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), + fieldOperand); } else { c->loadBarrier(); } @@ -2799,31 +3304,31 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } break; case iconst_m1: - frame->pushInt(c->constant(-1)); + frame->pushInt(c->constant(-1, IntField)); break; case iconst_0: - frame->pushInt(c->constant(0)); + frame->pushInt(c->constant(0, IntField)); break; case iconst_1: - frame->pushInt(c->constant(1)); + frame->pushInt(c->constant(1, IntField)); break; case iconst_2: - frame->pushInt(c->constant(2)); + frame->pushInt(c->constant(2, IntField)); break; case iconst_3: - frame->pushInt(c->constant(3)); + frame->pushInt(c->constant(3, IntField)); break; case iconst_4: - frame->pushInt(c->constant(4)); + frame->pushInt(c->constant(4, IntField)); break; case iconst_5: - frame->pushInt(c->constant(5)); + frame->pushInt(c->constant(5, IntField)); break; case idiv: { @@ -2906,46 +3411,66 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* target = frame->machineIp(newIp); Compiler::Operand* cont = frame->machineIp(ip); - if(lastFcmpl != 1 && lastFcmpg != 1) { + if (lastFcmpl != 1 && lastFcmpg != 1) { Compiler::Operand* a = frame->popInt(); - c->cmp(4, c->constant(0), a); + c->cmp(4, c->constant(0, IntField), a); } switch (instruction) { case ifeq: - if(lastFcmpl == 1 || lastFcmpg == 1) { - c->juo(cont); + if (lastFcmpl == 1 || lastFcmpg == 1) { + c->fjuo(cont); + c->fje(target); + } else { + c->je(target); } - c->je(target); break; case ifne: - if(lastFcmpl == 1 || lastFcmpg == 1) { - c->juo(cont); + if (lastFcmpl == 1 || lastFcmpg == 1) { + c->fjuo(cont); + c->fjne(target); + } else { + c->jne(target); } - c->jne(target); break; case ifgt: - if(lastFcmpl == 1) { - c->juo(cont); + if (lastFcmpl == 1) { + c->fjuo(cont); + c->fjg(target); + } else if (lastFcmpg == 1) { + c->fjg(target); + } else { + c->jg(target); } - c->jg(target); break; case ifge: - if(lastFcmpl == 1) { - c->juo(cont); + if (lastFcmpl == 1) { + c->fjuo(cont); + c->fjge(target); + } else if (lastFcmpg == 1) { + c->fjge(target); + } else { + c->jge(target); } - c->jge(target); break; case iflt: - if(lastFcmpg == 1) { - c->juo(cont); + if (lastFcmpg == 1) { + c->fjuo(cont); + c->fjl(target); + } else if (lastFcmpl == 1) { + c->fjl(target); + } else { + c->jl(target); } - c->jl(target); break; case ifle: if(lastFcmpg == 1) { - c->juo(cont); + c->fjuo(cont); + c->fjle(target); + } else if (lastFcmpl == 1) { + c->fjle(target); + } else { + c->jle(target); } - c->jle(target); break; } @@ -2962,7 +3487,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popObject(); Compiler::Operand* target = frame->machineIp(newIp); - c->cmp(BytesPerWord, c->constant(0), a); + c->cmp(BytesPerWord, c->constant(0, ObjectField), a); if (instruction == ifnull) { c->je(target); } else { @@ -2977,8 +3502,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint8_t index = codeBody(t, code, ip++); int8_t count = codeBody(t, code, ip++); - c->storeLocal - (1, c->add(4, c->constant(count), c->loadLocal(1, index)), index); + storeLocal + (context, 1, + c->add(4, c->constant(count, IntField), loadLocal(context, 1, index)), + index); } break; case iload: @@ -3024,9 +3551,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushInt (c->call - (c->constant(getThunk(t, instanceOf64Thunk)), - 0, 0, 4, - 3, c->thread(), frame->append(class_), frame->popObject())); + (c->constant(getThunk(t, instanceOf64Thunk), ObjectField), + 0, 0, 4, IntField, + 3, c->register_(t->arch->thread()), frame->append(class_), + frame->popObject())); } break; case invokeinterface: { @@ -3047,15 +3575,17 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* result = c->stackCall (c->call (c->constant - (getThunk(t, findInterfaceMethodFromInstanceThunk)), + (getThunk(t, findInterfaceMethodFromInstanceThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 3, c->thread(), frame->append(target), + ObjectField, + 3, c->register_(t->arch->thread()), frame->append(target), c->peek(1, instance)), 0, - frame->trace(target, true), + frame->trace(0, 0), rSize, + methodReturnCode(t, target), parameterFootprint); frame->pop(parameterFootprint); @@ -3067,7 +3597,6 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case invokespecial: { uint16_t index = codeReadInt16(t, code, ip); - object target = resolveMethod(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; @@ -3078,7 +3607,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, assert(t, (methodFlags(t, target) & ACC_STATIC) == 0); - compileDirectInvoke(t, frame, target); + bool tailCall = isTailCall(t, code, ip, context->method, target); + + compileDirectInvoke(t, frame, target, tailCall); } break; case invokestatic: { @@ -3088,43 +3619,45 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, if (UNLIKELY(t->exception)) return; assert(t, methodFlags(t, target) & ACC_STATIC); + + bool usedIntrinsic = false; int params = methodParameterCount(t, target); - if(params == 1) {//TODO: Get number of method params + if (params == 1) {//TODO: Get number of method params BinaryOperation op = t->arch->hasBinaryIntrinsic(t, target); - if(op != NoBinaryOperation) { - if(DebugIntrinsics) { + if (op != NoBinaryOperation) { + if (DebugIntrinsics) { fprintf(stderr, "Using binary intrinsic %i.\n", op); } int opSize = methodParameterFootprint(t, target) * BytesPerWord; int resSize = resultSize(t, methodReturnCode(t, target)); Compiler::Operand* param; - if(opSize == 4) { + if (opSize == 4) { param = frame->popInt(); } else { param = frame->popLong(); } - if(resSize == 4) { - frame->pushInt(c->operation(op, opSize, resSize, param)); + Compiler::Operand* operand = c->operation(op, opSize, resSize, methodReturnCode(t, target), param); + if (resSize == 4) { + frame->pushInt(operand); } else { - frame->pushLong(c->operation(op, opSize, resSize, param)); + frame->pushLong(operand); } - } else { - compileDirectInvoke(t, frame, target); + usedIntrinsic = true; } - } else if(params == 2) { //TODO: Get number of method params + } else if (params == 2) { //TODO: Get number of method params TernaryOperation op = t->arch->hasTernaryIntrinsic(t, target); - if(op != NoTernaryOperation) { - if(DebugIntrinsics) { + if (op != NoTernaryOperation) { + if (DebugIntrinsics) { fprintf(stderr, "Could use ternary intrinsic %i.\n", op); } //int aSize, bSize; //int resSize = resultSize(t, methodReturnCode(t, target)); - compileDirectInvoke(t, frame, target); //TODO: use intrinsic - } else { - compileDirectInvoke(t, frame, target); + //TODO: use intrinsic } - } else { - compileDirectInvoke(t, frame, target); + } + if (!usedIntrinsic) { + bool tailCall = isTailCall(t, code, ip, context->method, target); + compileDirectInvoke(t, frame, target, tailCall); } } break; @@ -3144,14 +3677,17 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, unsigned rSize = resultSize(t, methodReturnCode(t, target)); + bool tailCall = isTailCall(t, code, ip, context->method, target); + Compiler::Operand* result = c->stackCall (c->memory (c->and_ - (BytesPerWord, c->constant(PointerMask), - c->memory(instance, 0, 0, 1)), offset, 0, 1), - 0, - frame->trace(target, true), + (BytesPerWord, c->constant(PointerMask, ObjectField), + c->memory(instance, ObjectField, 0, 0, 1)), ObjectField, offset, 0, 1), + tailCall ? Compiler::TailJump : 0, + frame->trace(0, 0), rSize, + methodReturnCode(t, target), parameterFootprint); frame->pop(parameterFootprint); @@ -3251,30 +3787,14 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, assert(t, newIp < codeLength(t, code)); - int index = exceptionIndex(t, code, thisIp, newIp); - if (index >= 0) { - // store a null pointer at the same index the exception would - // be stored in the finally block so we can safely treat that - // location as a GC root. Of course, this assumes there - // wasn't already a live value there, which is something we - // should verify once we have complete data flow information - // (todo). - c->storeLocal(1, c->constant(0), index); - frame->storedObject(index); - } - - frame->pushAddress(frame->addressOperand(c->machineIp(ip))); + unsigned start = frame->startSubroutine(newIp, c->machineIp(ip)); c->jmp(frame->machineIp(newIp)); - frame->subroutine = c->startSubroutine(); - - compile(t, frame, newIp); + saveStateAndCompile(t, frame, newIp); if (UNLIKELY(t->exception)) return; - frame->poppedInt(); - - c->restoreFromSubroutine(frame->subroutine); + frame->endSubroutine(start); } break; case l2d: { @@ -3309,11 +3829,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } break; case lconst_0: - frame->pushLong(c->constant(0)); + frame->pushLong(c->constant(0, LongField)); break; case lconst_1: - frame->pushLong(c->constant(1)); + frame->pushLong(c->constant(1, LongField)); break; case ldc: @@ -3341,7 +3861,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushObject(frame->append(v)); } } else { - frame->pushInt(c->constant(singletonValue(t, pool, index - 1))); + frame->pushInt(c->constant(singletonValue(t, pool, index - 1), singletonIsFloat(t, pool, index - 1) ? FloatField : IntField)); } } break; @@ -3352,7 +3872,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint64_t v; memcpy(&v, &singletonValue(t, pool, index - 1), 8); - frame->pushLong(c->constant(v)); + frame->pushLong(c->constant(v, singletonIsFloat(t, pool, index - 1) ? FloatField : IntField)); } break; case ldiv_: { @@ -3431,9 +3951,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, c->jmp (c->call - (c->constant(getThunk(t, lookUpAddressThunk)), - 0, 0, BytesPerWord, - 4, key, start, c->constant(pairCount), default_)); + (c->constant(getThunk(t, lookUpAddressThunk), ObjectField), + 0, 0, BytesPerWord, ObjectField, + 4, key, start, c->constant(pairCount, IntField), default_)); Compiler::State* state = c->saveState(); @@ -3523,15 +4043,15 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case monitorenter: { Compiler::Operand* target = frame->popObject(); c->call - (c->constant(getThunk(t, acquireMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), target); + (c->constant(getThunk(t, acquireMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), target); } break; case monitorexit: { Compiler::Operand* target = frame->popObject(); c->call - (c->constant(getThunk(t, releaseMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), target); + (c->constant(getThunk(t, releaseMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), target); } break; case multianewarray: { @@ -3543,18 +4063,18 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, PROTECT(t, class_); unsigned offset - = (localOffset - (t, localSize(t, context->method) + c->index(c->top()), - context->method) / BytesPerWord) + = localOffset + (t, localSize(t, context->method) + c->topOfStack(), context->method) + t->arch->frameReturnAddressSize(); Compiler::Operand* result = c->call - (c->constant(getThunk(t, makeMultidimensionalArrayThunk)), + (c->constant(getThunk(t, makeMultidimensionalArrayThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 4, c->thread(), frame->append(class_), c->constant(dimensions), - c->constant(offset)); + ObjectField, + 4, c->register_(t->arch->thread()), frame->append(class_), + c->constant(dimensions, IntField), c->constant(offset, IntField)); frame->pop(dimensions); frame->pushObject(result); @@ -3566,22 +4086,24 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object class_ = resolveClassInPool(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; - if (classVmFlags(t, class_) & WeakReferenceFlag) { + if (classVmFlags(t, class_) & (WeakReferenceFlag | HasFinalizerFlag)) { frame->pushObject (c->call - (c->constant(getThunk(t, makeNewWeakReference64Thunk)), + (c->constant(getThunk(t, makeNewGeneral64Thunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 2, c->thread(), frame->append(class_))); + ObjectField, + 2, c->register_(t->arch->thread()), frame->append(class_))); } else { frame->pushObject (c->call - (c->constant(getThunk(t, makeNew64Thunk)), + (c->constant(getThunk(t, makeNew64Thunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 2, c->thread(), frame->append(class_))); + ObjectField, + 2, c->register_(t->arch->thread()), frame->append(class_))); } } break; @@ -3592,11 +4114,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->pushObject (c->call - (c->constant(getThunk(t, makeBlankArrayThunk)), + (c->constant(getThunk(t, makeBlankArrayThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), BytesPerWord, - 3, c->thread(), c->constant(type), length)); + ObjectField, + 3, c->register_(t->arch->thread()), c->constant(type, ObjectField), length)); } break; case nop: break; @@ -3625,11 +4148,13 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, and classNeedsInit(t, fieldClass(t, field))) { c->call - (c->constant(getThunk(t, tryInitClassThunk)), + (c->constant(getThunk(t, tryInitClassThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 2, c->thread(), frame->append(fieldClass(t, field))); + VoidField, + 2, c->register_(t->arch->thread()), + frame->append(fieldClass(t, field))); } staticTable = classStaticTable(t, fieldClass(t, field)); @@ -3682,8 +4207,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, fieldOperand = frame->append(field); c->call - (c->constant(getThunk(t, acquireMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), fieldOperand); + (c->constant(getThunk(t, acquireMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), + fieldOperand); } else { c->storeStoreBarrier(); } @@ -3693,39 +4219,42 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case ByteField: case BooleanField: c->store(BytesPerWord, value, 1, - c->memory(table, fieldOffset(t, field), 0, 1)); + c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1)); break; case CharField: case ShortField: c->store(BytesPerWord, value, 2, - c->memory(table, fieldOffset(t, field), 0, 1)); + c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1)); break; case FloatField: case IntField: c->store(BytesPerWord, value, 4, - c->memory(table, fieldOffset(t, field), 0, 1)); + c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1)); break; case DoubleField: case LongField: - c->store(8, value, 8, c->memory(table, fieldOffset(t, field), 0, 1)); + c->store(8, value, 8, c->memory(table, fieldCode(t, field), fieldOffset(t, field), 0, 1)); break; case ObjectField: if (instruction == putfield) { c->call - (c->constant(getThunk(t, setMaybeNullThunk)), + (c->constant(getThunk(t, setMaybeNullThunk), ObjectField), 0, - frame->trace(0, false), + frame->trace(0, 0), 0, - 4, c->thread(), table, c->constant(fieldOffset(t, field)), value); + VoidField, + 4, c->register_(t->arch->thread()), table, + c->constant(fieldOffset(t, field), IntField), value); } else { c->call - (c->constant(getThunk(t, setThunk)), - 0, 0, 0, - 4, c->thread(), table, c->constant(fieldOffset(t, field)), value); + (c->constant(getThunk(t, setThunk), ObjectField), + 0, 0, 0, VoidField, + 4, c->register_(t->arch->thread()), table, + c->constant(fieldOffset(t, field), IntField), value); } break; @@ -3738,24 +4267,24 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, or fieldCode(t, field) == LongField)) { c->call - (c->constant(getThunk(t, releaseMonitorForObjectThunk)), - 0, frame->trace(0, false), 0, 2, c->thread(), fieldOperand); + (c->constant(getThunk(t, releaseMonitorForObjectThunk), ObjectField), + 0, frame->trace(0, 0), 0, VoidField, 2, c->register_(t->arch->thread()), + fieldOperand); } else { c->storeLoadBarrier(); } } } break; - case ret: - c->jmp(c->loadLocal(1, codeBody(t, code, ip))); - c->endSubroutine(frame->subroutine); - return; + case ret: { + unsigned index = codeBody(t, code, ip); + c->saveLocals(); + c->jmp(loadLocal(context, 1, index)); + frame->returnFromSubroutine(index); + } return; case return_: - if ((methodFlags(t, context->method) & ConstructorFlag) - and (classFlags(t, methodClass(t, context->method)) - & HasFinalMemberFlag)) - { + if (needsReturnBarrier(t, context->method)) { c->storeStoreBarrier(); } @@ -3765,7 +4294,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case sipush: frame->pushInt - (c->constant(static_cast(codeReadInt16(t, code, ip)))); + (c->constant(static_cast(codeReadInt16(t, code, ip)), IntField)); break; case swap: @@ -3802,14 +4331,14 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* key = frame->popInt(); - c->cmp(4, c->constant(bottom), key); + c->cmp(4, c->constant(bottom, IntField), key); c->jl(frame->machineIp(defaultIp)); c->save(1, key); saveStateAndCompile(t, frame, defaultIp); - c->cmp(4, c->constant(top), key); + c->cmp(4, c->constant(top, IntField), key); c->jg(frame->machineIp(defaultIp)); c->save(1, key); @@ -3817,10 +4346,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, saveStateAndCompile(t, frame, defaultIp); Compiler::Operand* normalizedKey - = (bottom ? c->sub(4, c->constant(bottom), key) : key); + = (bottom ? c->sub(4, c->constant(bottom, IntField), key) : key); c->jmp(c->load(BytesPerWord, BytesPerWord, - c->memory(start, 0, normalizedKey, BytesPerWord), + c->memory(start, ObjectField, 0, normalizedKey, BytesPerWord), BytesPerWord)); Compiler::State* state = c->saveState(); @@ -3849,8 +4378,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint16_t index = codeReadInt16(t, code, ip); uint16_t count = codeReadInt16(t, code, ip); - c->storeLocal - (1, c->add(4, c->constant(count), c->loadLocal(1, index)), index); + storeLocal + (context, 1, + c->add(4, c->constant(count, IntField), loadLocal(context, 1, index)), + index); } break; case iload: { @@ -3869,10 +4400,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, frame->storeLong(codeReadInt16(t, code, ip)); } break; - case ret: - c->jmp(c->loadLocal(1, codeReadInt16(t, code, ip))); - c->endSubroutine(frame->subroutine); - return; + case ret: { + unsigned index = codeReadInt16(t, code, ip); + c->jmp(loadLocal(context, 1, index)); + frame->returnFromSubroutine(index); + } return; default: abort(t); } @@ -3883,24 +4415,25 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } } +FILE* compileLog = 0; + void logCompile(MyThread* t, const void* code, unsigned size, const char* class_, const char* name, const char* spec) { - static FILE* log = 0; static bool open = false; if (not open) { open = true; const char* path = findProperty(t, "avian.jit.log"); if (path) { - log = fopen(path, "wb"); + compileLog = fopen(path, "wb"); } else if (DebugCompile) { - log = stderr; + compileLog = stderr; } } - if (log) { - fprintf(log, "%p %p %s.%s%s\n", + if (compileLog) { + fprintf(compileLog, "%p %p %s.%s%s\n", code, static_cast(code) + size, class_, name, spec); } @@ -3994,7 +4527,7 @@ printSet(uintptr_t m, unsigned limit) unsigned calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, - unsigned stackPadding, unsigned eventIndex) + unsigned eventIndex, SubroutinePath* subroutinePath = 0) { // for each instruction with more than one predecessor, and for each // stack position, determine if there exists a path to that @@ -4002,7 +4535,6 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, // stack position (i.e. it is uninitialized or contains primitive // data). - unsigned localSize = ::localSize(t, context->method); unsigned mapSize = frameMapSizeInWords(t, context->method); uintptr_t roots[mapSize]; @@ -4028,7 +4560,7 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, switch (e) { case PushContextEvent: { eventIndex = calculateFrameMaps - (t, context, roots, stackPadding, eventIndex); + (t, context, roots, eventIndex, subroutinePath); } break; case PopContextEvent: @@ -4044,7 +4576,9 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, fprintf(stderr, "\n"); } - uintptr_t* tableRoots = context->rootTable + (ip * mapSize); + uintptr_t* tableRoots + = (subroutinePath ? subroutinePath->rootTable : context->rootTable) + + (ip * mapSize); if (context->visitTable[ip] > 1) { for (unsigned wi = 0; wi < mapSize; ++wi) { @@ -4079,10 +4613,6 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, unsigned i = context->eventLog.get2(eventIndex); eventIndex += 2; - if (i >= localSize) { - i += stackPadding; - } - markBit(roots, i); } break; @@ -4090,25 +4620,100 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, unsigned i = context->eventLog.get2(eventIndex); eventIndex += 2; - if (i >= localSize) { - i += stackPadding; - } - clearBit(roots, i); } break; + case PushExceptionHandlerEvent: { + unsigned reference = context->eventLog.get2(eventIndex); + eventIndex += 2; + + if (context->subroutineTable and context->subroutineTable[reference]) { + Subroutine* s = context->subroutineTable[reference]; + unsigned originalEventIndex = eventIndex; + + for (SubroutineCall* c = s->calls; c; c = c->next) { + for (SubroutinePath* p = c->paths; p; p = p->listNext) { + memcpy(roots, p->rootTable + (reference * mapSize), + mapSize * BytesPerWord); + + eventIndex = calculateFrameMaps + (t, context, roots, originalEventIndex, p); + } + } + } else { + memcpy(roots, context->rootTable + (reference * mapSize), + mapSize * BytesPerWord); + + eventIndex = calculateFrameMaps(t, context, roots, eventIndex, 0); + } + } break; + case TraceEvent: { TraceElement* te; context->eventLog.get(eventIndex, &te, BytesPerWord); if (DebugFrameMaps) { fprintf(stderr, "trace roots at ip %3d: ", ip); printSet(*roots, mapSize); + if (subroutinePath) { + fprintf(stderr, " "); + print(subroutinePath); + } fprintf(stderr, "\n"); } - memcpy(te->map, roots, mapSize * BytesPerWord); + + if (subroutinePath == 0) { + memcpy(te->map, roots, mapSize * BytesPerWord); + } else { + SubroutineTrace* trace = 0; + for (SubroutineTrace* t = te->subroutineTrace; t; t = t->next) { + if (t->path == subroutinePath) { + trace = t; + } + } + + if (trace == 0) { + te->subroutineTrace = trace = new + (context->zone.allocate + (sizeof(SubroutineTrace) + (mapSize * BytesPerWord))) + SubroutineTrace(subroutinePath, te->subroutineTrace); + + ++ te->subroutineTraceCount; + } + + memcpy(trace->map, roots, mapSize * BytesPerWord); + } eventIndex += BytesPerWord; } break; + case PushSubroutineEvent: { + SubroutineCall* call; + context->eventLog.get(eventIndex, &call, BytesPerWord); + eventIndex += BytesPerWord; + + unsigned nextIndex = context->eventLog.get2(eventIndex); + + eventIndex = nextIndex; + + SubroutinePath* path = 0; + for (SubroutinePath* p = call->paths; p; p = p->listNext) { + if (p->stackNext == subroutinePath) { + path = p; + break; + } + } + + if (path == 0) { + path = new (context->zone.allocate(sizeof(SubroutinePath))) + SubroutinePath(call, subroutinePath, + makeRootTable(t, &(context->zone), context->method)); + } + + calculateFrameMaps(t, context, roots, call->subroutine->logIndex, path); + } break; + + case PopSubroutineEvent: + return static_cast(-1); + default: abort(t); } } @@ -4116,9 +4721,6 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, return eventIndex; } -Zone* -codeZone(MyThread* t); - int compareTraceElementPointers(const void* va, const void* vb) { @@ -4134,9 +4736,9 @@ compareTraceElementPointers(const void* va, const void* vb) } unsigned -frameObjectMapSize(MyThread* t, object method, object map) +simpleFrameMapTableSize(MyThread* t, object method, object map) { - int size = usableFrameSizeWithParameters(t, method); + int size = frameMapSizeInBits(t, method); return ceiling(intArrayLength(t, map) * size, 32 + size); } @@ -4162,21 +4764,266 @@ finish(MyThread* t, Allocator* allocator, Assembler* a, const char* name) } void -setBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i, - unsigned j) +setBit(int32_t* dst, unsigned index) { - unsigned index = ((i * size) + j); - intArrayBody(t, map, count + (index / 32)) - |= static_cast(1) << (index % 32); + dst[index / 32] |= static_cast(1) << (index % 32); } void -clearBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i, - unsigned j) +clearBit(int32_t* dst, unsigned index) { - unsigned index = ((i * size) + j); - intArrayBody(t, map, count + (index / 32)) - &= ~(static_cast(1) << (index % 32)); + dst[index / 32] &= ~(static_cast(1) << (index % 32)); +} + +void +copyFrameMap(int32_t* dst, uintptr_t* src, unsigned mapSizeInBits, + unsigned offset, TraceElement* p, + SubroutinePath* subroutinePath) +{ + if (DebugFrameMaps) { + fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast + (p->address->value())); + printSet(src[0], ceiling(mapSizeInBits, BitsPerWord)); + print(subroutinePath); + fprintf(stderr, "\n"); + + fprintf(stderr, "final roots at ip %p: ", reinterpret_cast + (p->address->value())); + } + + for (unsigned j = 0; j < p->argumentIndex; ++j) { + if (getBit(src, j)) { + if (DebugFrameMaps) { + fprintf(stderr, "1"); + } + setBit(dst, offset + j); + } else { + if (DebugFrameMaps) { + fprintf(stderr, "_"); + } + clearBit(dst, offset + j); + } + } + + if (DebugFrameMaps) { + print(subroutinePath); + fprintf(stderr, "\n"); + } +} + +class FrameMapTableHeader { + public: + FrameMapTableHeader(unsigned indexCount): + indexCount(indexCount) + { } + + unsigned indexCount; +}; + +class FrameMapTableIndexElement { + public: + FrameMapTableIndexElement(int offset, unsigned base, unsigned path): + offset(offset), + base(base), + path(path) + { } + + int offset; + unsigned base; + unsigned path; +}; + +class FrameMapTablePath { + public: + FrameMapTablePath(unsigned stackIndex, unsigned elementCount, unsigned next): + stackIndex(stackIndex), + elementCount(elementCount), + next(next) + { } + + unsigned stackIndex; + unsigned elementCount; + unsigned next; + int32_t elements[0]; +}; + +int +compareInt32s(const void* va, const void* vb) +{ + return *static_cast(va) - *static_cast(vb); +} + +int +compare(SubroutinePath* a, SubroutinePath* b) +{ + if (a->stackNext) { + int d = compare(a->stackNext, b->stackNext); + if (d) return d; + } + int64_t av = a->call->returnAddress->value(); + int64_t bv = b->call->returnAddress->value(); + if (av > bv) { + return 1; + } else if (av < bv) { + return -1; + } else { + return 0; + } +} + +int +compareSubroutineTracePointers(const void* va, const void* vb) +{ + return compare((*static_cast(va))->path, + (*static_cast(vb))->path); +} + +object +makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start, + TraceElement** elements, unsigned pathFootprint, + unsigned mapCount) +{ + unsigned mapSize = frameMapSizeInBits(t, context->method); + unsigned indexOffset = sizeof(FrameMapTableHeader); + unsigned mapsOffset = indexOffset + + (context->traceLogCount * sizeof(FrameMapTableIndexElement)); + unsigned pathsOffset = mapsOffset + (ceiling(mapCount * mapSize, 32) * 4); + + object table = makeByteArray(t, pathsOffset + pathFootprint); + + int8_t* body = &byteArrayBody(t, table, 0); + new (body) FrameMapTableHeader(context->traceLogCount); + + unsigned nextTableIndex = pathsOffset; + unsigned nextMapIndex = 0; + for (unsigned i = 0; i < context->traceLogCount; ++i) { + TraceElement* p = elements[i]; + unsigned mapBase = nextMapIndex; + + unsigned pathIndex; + if (p->subroutineTrace) { + FrameMapTablePath* previous = 0; + Subroutine* subroutine = p->subroutineTrace->path->call->subroutine; + for (Subroutine* s = subroutine; s; s = s->stackNext) { + if (s->tableIndex == 0) { + unsigned pathObjectSize = sizeof(FrameMapTablePath) + + (sizeof(int32_t) * s->callCount); + + assert(t, nextTableIndex + pathObjectSize + <= byteArrayLength(t, table)); + + s->tableIndex = nextTableIndex; + + nextTableIndex += pathObjectSize; + + FrameMapTablePath* current = new (body + s->tableIndex) + FrameMapTablePath + (s->stackIndex, s->callCount, + s->stackNext ? s->stackNext->tableIndex : 0); + + unsigned i = 0; + for (SubroutineCall* c = subroutine->calls; c; c = c->next) { + assert(t, i < s->callCount); + + current->elements[i++] + = static_cast(c->returnAddress->value()) + - reinterpret_cast(start); + } + assert(t, i == s->callCount); + + qsort(current->elements, s->callCount, sizeof(int32_t), + compareInt32s); + + if (previous) { + previous->next = s->tableIndex; + } + + previous = current; + } else { + break; + } + } + + pathIndex = subroutine->tableIndex; + + SubroutineTrace* traces[p->subroutineTraceCount]; + unsigned i = 0; + for (SubroutineTrace* trace = p->subroutineTrace; + trace; trace = trace->next) + { + assert(t, i < p->subroutineTraceCount); + traces[i++] = trace; + } + assert(t, i == p->subroutineTraceCount); + + qsort(traces, p->subroutineTraceCount, sizeof(SubroutineTrace*), + compareSubroutineTracePointers); + + for (unsigned i = 0; i < p->subroutineTraceCount; ++i) { + assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + <= pathsOffset); + + copyFrameMap(reinterpret_cast(body + mapsOffset), + traces[i]->map, mapSize, nextMapIndex, p, + traces[i]->path); + + nextMapIndex += mapSize; + } + } else { + pathIndex = 0; + + assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + <= pathsOffset); + + copyFrameMap(reinterpret_cast(body + mapsOffset), p->map, + mapSize, nextMapIndex, p, 0); + + nextMapIndex += mapSize; + } + + unsigned elementIndex = indexOffset + + (i * sizeof(FrameMapTableIndexElement)); + + assert(t, elementIndex + sizeof(FrameMapTableIndexElement) <= mapsOffset); + + new (body + elementIndex) FrameMapTableIndexElement + (static_cast(p->address->value()) + - reinterpret_cast(start), mapBase, pathIndex); + } + + assert(t, nextMapIndex == mapCount * mapSize); + + return table; +} + +object +makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start, + TraceElement** elements) +{ + unsigned mapSize = frameMapSizeInBits(t, context->method); + object table = makeIntArray + (t, context->traceLogCount + + ceiling(context->traceLogCount * mapSize, 32)); + + assert(t, intArrayLength(t, table) == context->traceLogCount + + simpleFrameMapTableSize(t, context->method, table)); + + for (unsigned i = 0; i < context->traceLogCount; ++i) { + TraceElement* p = elements[i]; + + intArrayBody(t, table, i) = static_cast(p->address->value()) + - reinterpret_cast(start); + + assert(t, context->traceLogCount + ceiling((i + 1) * mapSize, 32) + <= intArrayLength(t, table)); + + if (mapSize) { + copyFrameMap(&intArrayBody(t, table, context->traceLogCount), p->map, + mapSize, i * mapSize, p, 0); + } + } + + return table; } uint8_t* finish(MyThread* t, Allocator* allocator, Context* context) @@ -4246,76 +5093,47 @@ finish(MyThread* t, Allocator* allocator, Context* context) if (context->traceLogCount) { TraceElement* elements[context->traceLogCount]; unsigned index = 0; + unsigned pathFootprint = 0; + unsigned mapCount = 0; for (TraceElement* p = context->traceLog; p; p = p->next) { assert(t, index < context->traceLogCount); + SubroutineTrace* trace = p->subroutineTrace; + unsigned myMapCount = 1; + if (trace) { + for (Subroutine* s = trace->path->call->subroutine; + s; s = s->stackNext) + { + unsigned callCount = s->callCount; + myMapCount *= callCount; + if (not s->visited) { + s->visited = true; + pathFootprint += sizeof(FrameMapTablePath) + + (sizeof(int32_t) * callCount); + } + } + } + + mapCount += myMapCount; + elements[index++] = p; if (p->target) { insertCallNode (t, makeCallNode - (t, p->address->value(), p->target, p->virtualCall, 0)); + (t, p->address->value(), p->target, p->flags, 0)); } } qsort(elements, context->traceLogCount, sizeof(TraceElement*), compareTraceElementPointers); - unsigned size = usableFrameSizeWithParameters(t, context->method); - object map = makeIntArray - (t, context->traceLogCount - + ceiling(context->traceLogCount * size, 32)); - - assert(t, intArrayLength(t, map) == context->traceLogCount - + frameObjectMapSize(t, context->method, map)); - - for (unsigned i = 0; i < context->traceLogCount; ++i) { - TraceElement* p = elements[i]; - - intArrayBody(t, map, i) = static_cast(p->address->value()) - - reinterpret_cast(start); - - if (DebugFrameMaps) { - fprintf(stderr, " orig roots at ip %p: ", reinterpret_cast - (p->address->value())); - printSet(p->map[0], frameMapSizeInWords(t, context->method)); - fprintf(stderr, "\n"); - - fprintf(stderr, "final roots at ip %p: ", reinterpret_cast - (p->address->value())); - } - - for (unsigned j = 0, k = 0; j < size; ++j, ++k) { - if (j == p->padIndex) { - unsigned limit = j + p->padding; - assert(t, limit <= size); - - for (; j < limit; ++j) { - if (DebugFrameMaps) { - fprintf(stderr, "_"); - } - clearBit(t, map, context->traceLogCount, size, i, j); - } - - if (j == size) break; - } - - if (getBit(p->map, k)) { - if (DebugFrameMaps) { - fprintf(stderr, "1"); - } - setBit(t, map, context->traceLogCount, size, i, j); - } else { - if (DebugFrameMaps) { - fprintf(stderr, "_"); - } - clearBit(t, map, context->traceLogCount, size, i, j); - } - } - - if (DebugFrameMaps) { - fprintf(stderr, "\n"); - } + object map; + if (pathFootprint) { + map = makeGeneralFrameMapTable + (t, context, start, elements, pathFootprint, mapCount); + } else { + map = makeSimpleFrameMapTable(t, context, start, elements); } set(t, methodCode(t, context->method), CodePool, map); @@ -4335,11 +5153,11 @@ finish(MyThread* t, Allocator* allocator, Context* context) strcmp (reinterpret_cast (&byteArrayBody(t, className(t, methodClass(t, context->method)), 0)), - "java/lang/Throwable") == 0 and + "java/io/FileOutputStream") == 0 and strcmp (reinterpret_cast (&byteArrayBody(t, methodName(t, context->method), 0)), - "printStackTrace") == 0) + "write") == 0) { trap(); printf("Address: %p\n", ::vmAddressFromLine(t, (object)(context->method), 1176)); @@ -4368,10 +5186,10 @@ compile(MyThread* t, Allocator* allocator, Context* context) uint8_t stackMap[codeMaxStack(t, methodCode(t, context->method))]; Frame frame(context, stackMap); - unsigned index = 0; + unsigned index = methodParameterFootprint(t, context->method); if ((methodFlags(t, context->method) & ACC_STATIC) == 0) { - c->initLocal(1, index); - frame.set(index++, Frame::Object); + frame.set(--index, Frame::Object); + c->initLocal(1, index, ObjectField); } for (MethodSpecIterator it @@ -4382,20 +5200,29 @@ compile(MyThread* t, Allocator* allocator, Context* context) switch (*it.next()) { case 'L': case '[': - c->initLocal(1, index); - frame.set(index++, Frame::Object); + frame.set(--index, Frame::Object); + c->initLocal(1, index, ObjectField); break; case 'J': - case 'D': - c->initLocal(2, index); - frame.set(index++, Frame::Long); - frame.set(index++, Frame::Long); + frame.set(--index, Frame::Long); + frame.set(--index, Frame::Long); + c->initLocal(2, index, LongField); break; - + case 'D': + frame.set(--index, Frame::Long); + frame.set(--index, Frame::Long); + c->initLocal(2, index, DoubleField); + break; + + case 'F': + frame.set(--index, Frame::Integer); + c->initLocal(1, index, FloatField); + break; + default: - c->initLocal(1, index); - frame.set(index++, Frame::Integer); + frame.set(--index, Frame::Integer); + c->initLocal(1, index, IntField); break; } } @@ -4408,7 +5235,7 @@ compile(MyThread* t, Allocator* allocator, Context* context) if (UNLIKELY(t->exception)) return 0; context->dirtyRoots = false; - unsigned eventIndex = calculateFrameMaps(t, context, 0, 0, 0); + unsigned eventIndex = calculateFrameMaps(t, context, 0, 0); object eht = codeExceptionHandlerTable(t, methodCode(t, context->method)); if (eht) { @@ -4436,16 +5263,8 @@ compile(MyThread* t, Allocator* allocator, Context* context) uint8_t stackMap[codeMaxStack(t, methodCode(t, context->method))]; Frame frame2(&frame, stackMap); - uintptr_t* roots = context->rootTable - + (start * frameMapSizeInWords(t, context->method)); - - for (unsigned i = 0; i < localSize(t, context->method); ++ i) { - if (getBit(roots, i)) { - frame2.set(i, Frame::Object); - } else { - frame2.set(i, Frame::Integer); - } - } + context->eventLog.append(PushExceptionHandlerEvent); + context->eventLog.append2(start); for (unsigned i = 1; i < codeMaxStack(t, methodCode(t, context->method)); @@ -4457,7 +5276,9 @@ compile(MyThread* t, Allocator* allocator, Context* context) compile(t, &frame2, exceptionHandlerIp(eh), start); if (UNLIKELY(t->exception)) return 0; - eventIndex = calculateFrameMaps(t, context, 0, 0, eventIndex); + context->eventLog.append(PopContextEvent); + + eventIndex = calculateFrameMaps(t, context, 0, eventIndex); } } @@ -4467,7 +5288,7 @@ compile(MyThread* t, Allocator* allocator, Context* context) while (context->dirtyRoots) { context->dirtyRoots = false; - calculateFrameMaps(t, context, 0, 0, 0); + calculateFrameMaps(t, context, 0, 0); } return finish(t, allocator, context); @@ -4480,41 +5301,32 @@ updateCall(MyThread* t, UnaryOperation op, bool assertAlignment, t->arch->updateCall(op, assertAlignment, returnAddress, target); } -void -compile(MyThread* t, Allocator* allocator, BootContext* bootContext, - object method); - void* -compileMethod2(MyThread* t) +compileMethod2(MyThread* t, void* ip) { - object node = findCallNode(t, t->arch->frameIp(t->stack)); - PROTECT(t, node); - + object node = findCallNode(t, ip); object target = callNodeTarget(t, node); - PROTECT(t, target); - - if (callNodeVirtualCall(t, node)) { - target = resolveTarget(t, t->stack, target); - } if (LIKELY(t->exception == 0)) { - compile(t, codeZone(t), 0, target); + PROTECT(t, node); + PROTECT(t, target); + + t->trace->targetMethod = target; + + compile(t, codeAllocator(t), 0, target); + + t->trace->targetMethod = 0; } if (UNLIKELY(t->exception)) { return 0; } else { void* address = reinterpret_cast(methodAddress(t, target)); - if (callNodeVirtualCall(t, node)) { - classVtable - (t, objectClass - (t, resolveThisPointer(t, t->stack, target)), methodOffset(t, target)) - = address; - } else { - updateCall - (t, Call, true, reinterpret_cast(callNodeAddress(t, node)), - address); - } + uint8_t* updateIp = static_cast(ip); + + updateCall(t, (callNodeFlags(t, node) & TraceElement::TailCall) + ? Jump : Call, true, updateIp, address); + return address; } } @@ -4522,7 +5334,15 @@ compileMethod2(MyThread* t) uint64_t compileMethod(MyThread* t) { - void* r = compileMethod2(t); + void* ip; + if (t->tailAddress) { + ip = t->tailAddress; + t->tailAddress = 0; + } else { + ip = t->arch->frameIp(t->stack); + } + + void* r = compileMethod2(t, ip); if (UNLIKELY(t->exception)) { unwind(t); @@ -4531,17 +5351,75 @@ compileMethod(MyThread* t) } } +void* +compileVirtualMethod2(MyThread* t, object class_, unsigned index) +{ + // If class_ has BootstrapFlag set, that means its vtable is not yet + // available. However, we must set t->trace->targetMethod to an + // appropriate method to ensure we can accurately scan the stack for + // GC roots. We find such a method by looking for a superclass with + // a vtable and using it instead: + + object c = class_; + while (classVmFlags(t, c) & BootstrapFlag) { + c = classSuper(t, c); + } + t->trace->targetMethod = arrayBody(t, classVirtualTable(t, c), index); + + PROTECT(t, class_); + + object target = resolveTarget(t, class_, index); + PROTECT(t, target); + + if (LIKELY(t->exception == 0)) { + compile(t, codeAllocator(t), 0, target); + } + + t->trace->targetMethod = 0; + + if (UNLIKELY(t->exception)) { + return 0; + } else { + void* address = reinterpret_cast(methodAddress(t, target)); + if (methodFlags(t, target) & ACC_NATIVE) { + t->trace->nativeMethod = target; + } else { + classVtable(t, class_, methodOffset(t, target)) = address; + } + return address; + } +} + uint64_t -invokeNative2(MyThread* t, object method) +compileVirtualMethod(MyThread* t) +{ + object class_ = objectClass(t, static_cast(t->virtualCallTarget)); + t->virtualCallTarget = 0; + + unsigned index = t->virtualCallIndex; + t->virtualCallIndex = 0; + + void* r = compileVirtualMethod2(t, class_, index); + + if (UNLIKELY(t->exception)) { + unwind(t); + } else { + return reinterpret_cast(r); + } +} + +void +resolveNative(MyThread* t, object method) { PROTECT(t, method); assert(t, methodFlags(t, method) & ACC_NATIVE); initClass(t, methodClass(t, method)); - if (UNLIKELY(t->exception)) return 0; - if (methodCompiled(t, method) == defaultThunk(t)) { + if (LIKELY(t->exception == 0) + and methodCompiled(t, method) == defaultThunk(t)) + { void* function = resolveNativeMethod(t, method); if (UNLIKELY(function == 0)) { object message = makeString @@ -4550,11 +5428,33 @@ invokeNative2(MyThread* t, object method) &byteArrayBody(t, methodName(t, method), 0), &byteArrayBody(t, methodSpec(t, method), 0)); t->exception = makeUnsatisfiedLinkError(t, message); - return 0; + return; } + // ensure other threads see updated methodVmFlags before + // methodCompiled, since we don't want them using the slow calling + // convention on a function that expects the fast calling + // convention: + memoryBarrier(); + methodCompiled(t, method) = reinterpret_cast(function); } +} + +uint64_t +invokeNativeFast(MyThread* t, object method) +{ + return reinterpret_cast(methodCompiled(t, method)) + (t, method, + static_cast(t->stack) + + t->arch->frameFooterSize() + + t->arch->frameReturnAddressSize()); +} + +uint64_t +invokeNativeSlow(MyThread* t, object method) +{ + PROTECT(t, method); object class_ = methodClass(t, method); PROTECT(t, class_); @@ -4574,12 +5474,13 @@ invokeNative2(MyThread* t, object method) types[typeOffset++] = POINTER_TYPE; uintptr_t* sp = static_cast(t->stack) - + parameterOffset(t, method); + + t->arch->frameFooterSize() + + t->arch->frameReturnAddressSize(); if (methodFlags(t, method) & ACC_STATIC) { args[argOffset++] = reinterpret_cast(&class_); } else { - args[argOffset++] = reinterpret_cast(sp--); + args[argOffset++] = reinterpret_cast(sp++); } types[typeOffset++] = POINTER_TYPE; @@ -4596,14 +5497,14 @@ invokeNative2(MyThread* t, object method) case INT16_TYPE: case INT32_TYPE: case FLOAT_TYPE: - args[argOffset++] = *(sp--); + args[argOffset++] = *(sp++); break; case INT64_TYPE: case DOUBLE_TYPE: { - memcpy(args + argOffset, sp - 1, 8); + memcpy(args + argOffset, sp, 8); argOffset += (8 / BytesPerWord); - sp -= 2; + sp += 2; } break; case POINTER_TYPE: { @@ -4612,7 +5513,7 @@ invokeNative2(MyThread* t, object method) } else { args[argOffset++] = 0; } - -- sp; + ++ sp; } break; default: abort(t); @@ -4710,50 +5611,94 @@ invokeNative2(MyThread* t, object method) return result; } + +uint64_t +invokeNative2(MyThread* t, object method) +{ + if (methodVmFlags(t, method) & FastNative) { + return invokeNativeFast(t, method); + } else { + return invokeNativeSlow(t, method); + } +} uint64_t invokeNative(MyThread* t) { if (t->trace->nativeMethod == 0) { - object node = findCallNode(t, t->arch->frameIp(t->stack)); + void* ip; + if (t->tailAddress) { + ip = t->tailAddress; + t->tailAddress = 0; + } else { + ip = t->arch->frameIp(t->stack); + } + + object node = findCallNode(t, ip); object target = callNodeTarget(t, node); - if (callNodeVirtualCall(t, node)) { + if (callNodeFlags(t, node) & TraceElement::VirtualCall) { target = resolveTarget(t, t->stack, target); } t->trace->nativeMethod = target; } + assert(t, t->tailAddress == 0); + uint64_t result = 0; + t->trace->targetMethod = t->trace->nativeMethod; + if (LIKELY(t->exception == 0)) { - result = invokeNative2(t, t->trace->nativeMethod); + resolveNative(t, t->trace->nativeMethod); + + if (LIKELY(t->exception == 0)) { + result = invokeNative2(t, t->trace->nativeMethod); + } } + unsigned parameterFootprint = methodParameterFootprint + (t, t->trace->targetMethod); + + t->trace->targetMethod = 0; t->trace->nativeMethod = 0; if (UNLIKELY(t->exception)) { unwind(t); } else { + if (TailCalls + and t->arch->argumentFootprint(parameterFootprint) + > t->arch->stackAlignmentInWords()) + { + t->stack = static_cast(t->stack) + + (t->arch->argumentFootprint(parameterFootprint) + - t->arch->stackAlignmentInWords()); + } + + t->stack = static_cast(t->stack) + + t->arch->frameReturnAddressSize(); + return result; } } -unsigned -frameMapIndex(MyThread* t, object method, int32_t offset) +void +findFrameMapInSimpleTable(MyThread* t, object method, object table, + int32_t offset, int32_t** map, unsigned* start) { - object map = codePool(t, methodCode(t, method)); - unsigned mapSize = frameObjectMapSize(t, method, map); - unsigned indexSize = intArrayLength(t, map) - mapSize; + unsigned tableSize = simpleFrameMapTableSize(t, method, table); + unsigned indexSize = intArrayLength(t, table) - tableSize; + + *map = &intArrayBody(t, table, indexSize); unsigned bottom = 0; unsigned top = indexSize; for (unsigned span = top - bottom; span; span = top - bottom) { unsigned middle = bottom + (span / 2); - int32_t v = intArrayBody(t, map, middle); + int32_t v = intArrayBody(t, table, middle); if (offset == v) { - return (indexSize * 32) - + (usableFrameSizeWithParameters(t, method) * middle); + *start = frameMapSizeInBits(t, method) * middle; + return; } else if (offset < v) { top = middle; } else { @@ -4764,34 +5709,142 @@ frameMapIndex(MyThread* t, object method, int32_t offset) abort(t); } -void -visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* stack, object method, - void* ip, void* calleeStack, unsigned argumentFootprint) +unsigned +findFrameMap(MyThread* t, void* stack, object method, object table, + unsigned pathIndex) { - unsigned count; - if (calleeStack) { - count = usableFrameSizeWithParameters(t, method) - argumentFootprint; + if (pathIndex) { + FrameMapTablePath* path = reinterpret_cast + (&byteArrayBody(t, table, pathIndex)); + + void* address = static_cast(stack)[path->stackIndex]; + uint8_t* base = reinterpret_cast(methodAddress(t, method)); + for (unsigned i = 0; i < path->elementCount; ++i) { + if (address == base + path->elements[i]) { + return i + (path->elementCount * findFrameMap + (t, stack, method, table, path->next)); + } + } + + abort(t); } else { - count = usableFrameSizeWithParameters(t, method); + return 0; } - +} + +void +findFrameMapInGeneralTable(MyThread* t, void* stack, object method, + object table, int32_t offset, int32_t** map, + unsigned* start) +{ + FrameMapTableHeader* header = reinterpret_cast + (&byteArrayBody(t, table, 0)); + + FrameMapTableIndexElement* index + = reinterpret_cast + (&byteArrayBody(t, table, sizeof(FrameMapTableHeader))); + + *map = reinterpret_cast(index + header->indexCount); + + unsigned bottom = 0; + unsigned top = header->indexCount; + for (unsigned span = top - bottom; span; span = top - bottom) { + unsigned middle = bottom + (span / 2); + FrameMapTableIndexElement* v = index + middle; + + if (offset == v->offset) { + *start = v->base + (findFrameMap(t, stack, method, table, v->path) + * frameMapSizeInBits(t, method)); + return; + } else if (offset < v->offset) { + top = middle; + } else { + bottom = middle + 1; + } + } + + abort(t); +} + +void +findFrameMap(MyThread* t, void* stack, object method, int32_t offset, + int32_t** map, unsigned* start) +{ + object table = codePool(t, methodCode(t, method)); + if (objectClass(t, table) + == arrayBody(t, t->m->types, Machine::IntArrayType)) + { + findFrameMapInSimpleTable(t, method, table, offset, map, start); + } else { + findFrameMapInGeneralTable(t, stack, method, table, offset, map, start); + } +} + +void +visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* frame, object method, + void* ip) +{ + unsigned count = frameMapSizeInBits(t, method); + if (count) { - object map = codePool(t, methodCode(t, method)); - int index = frameMapIndex - (t, method, difference - (ip, reinterpret_cast(methodAddress(t, method)))); + void* stack = stackForFrame(t, frame, method); + + int32_t* map; + unsigned offset; + findFrameMap + (t, stack, method, difference + (ip, reinterpret_cast(methodAddress(t, method))), &map, &offset); for (unsigned i = 0; i < count; ++i) { - int j = index + i; - if ((intArrayBody(t, map, j / 32) - & (static_cast(1) << (j % 32)))) - { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { v->visit(localObject(t, stack, method, i)); } } } } +void +visitArgument(MyThread* t, Heap::Visitor* v, void* stack, unsigned index) +{ + v->visit(static_cast(stack) + + index + + t->arch->frameReturnAddressSize() + + t->arch->frameFooterSize()); +} + +void +visitArguments(MyThread* t, Heap::Visitor* v, void* stack, object method) +{ + unsigned index = 0; + + if ((methodFlags(t, method) & ACC_STATIC) == 0) { + visitArgument(t, v, stack, index++); + } + + for (MethodSpecIterator it + (t, reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0))); + it.hasNext();) + { + switch (*it.next()) { + case 'L': + case '[': + visitArgument(t, v, stack, index++); + break; + + case 'J': + case 'D': + index += 2; + break; + + default: + ++ index; + break; + } + } +} + void visitStack(MyThread* t, Heap::Visitor* v) { @@ -4803,35 +5856,395 @@ visitStack(MyThread* t, Heap::Visitor* v) } MyThread::CallTrace* trace = t->trace; - void* calleeStack = 0; - unsigned argumentFootprint = 0; + object targetMethod = (trace ? trace->targetMethod : 0); while (stack) { + if (targetMethod) { + visitArguments(t, v, stack, targetMethod); + targetMethod = 0; + } + object method = methodForIp(t, ip); if (method) { PROTECT(t, method); - visitStackAndLocals - (t, v, stack, method, ip, calleeStack, argumentFootprint); - - calleeStack = stack; - argumentFootprint = methodParameterFootprint(t, method); - t->arch->nextFrame(&stack, &base); + + visitStackAndLocals(t, v, stack, method, ip); + ip = t->arch->frameIp(stack); } else if (trace) { - calleeStack = 0; - argumentFootprint = 0; stack = trace->stack; base = trace->base; ip = t->arch->frameIp(stack); trace = trace->next; + + if (trace) { + targetMethod = trace->targetMethod; + } } else { break; } } } +void +walkContinuationBody(MyThread* t, Heap::Walker* w, object c, int start) +{ + const int BodyOffset = ContinuationBody / BytesPerWord; + + object method = static_cast + (t->m->heap->follow(continuationMethod(t, c))); + int count = frameMapSizeInBits(t, method); + + if (count) { + int stack = BodyOffset + + (continuationFramePointerOffset(t, c) / BytesPerWord) + - t->arch->framePointerOffset() + - stackOffsetFromFrame(t, method); + + int first = stack + localOffsetFromStack(t, count - 1, method); + if (start > first) { + count -= start - first; + } + + int32_t* map; + unsigned offset; + findFrameMap + (t, reinterpret_cast(c) + stack, method, difference + (continuationAddress(t, c), + reinterpret_cast(methodAddress(t, method))), &map, &offset); + + for (int i = count - 1; i >= 0; --i) { + int j = offset + i; + if (map[j / 32] & (static_cast(1) << (j % 32))) { + if (not w->visit(stack + localOffsetFromStack(t, i, method))) { + return; + } + } + } + } +} + +void +callContinuation(MyThread* t, object continuation, object result, + object exception, void* ip, void* base, void* stack) +{ + assert(t, t->exception == 0); + + t->continuation = continuation; + + if (exception) { + t->exception = exception; + + t->ip = ip; + t->base = base; + t->stack = stack; + + findUnwindTarget(t, &ip, &base, &stack); + + t->ip = 0; + } + + t->trace->nativeMethod = 0; + t->trace->targetMethod = 0; + + vmJump(ip, base, stack, t, reinterpret_cast(result), 0); +} + +int8_t* +returnSpec(MyThread* t, object method) +{ + int8_t* s = &byteArrayBody(t, methodSpec(t, method), 0); + while (*s and *s != ')') ++ s; + expect(t, *s == ')'); + return s + 1; +} + +object +returnClass(MyThread* t, object method) +{ + int8_t* spec = returnSpec(t, method); + unsigned length = strlen(reinterpret_cast(spec)); + object name; + if (*spec == '[') { + name = makeByteArray(t, length + 1); + memcpy(&byteArrayBody(t, name, 0), spec, length); + } else { + assert(t, *spec == 'L'); + assert(t, spec[length - 1] == ';'); + name = makeByteArray(t, length - 1); + memcpy(&byteArrayBody(t, name, 0), spec + 1, length - 2); + } + return resolveClass(t, name); +} + +bool +compatibleReturnType(MyThread* t, object oldMethod, object newMethod) +{ + if (oldMethod == newMethod) { + return true; + } else if (methodReturnCode(t, oldMethod) == methodReturnCode(t, newMethod)) + { + if (methodReturnCode(t, oldMethod) == ObjectField) { + PROTECT(t, newMethod); + + object oldClass = returnClass(t, oldMethod); + PROTECT(t, oldClass); + + object newClass = returnClass(t, newMethod); + + return isAssignableFrom(t, oldClass, newClass); + } else { + return true; + } + } else { + return methodReturnCode(t, oldMethod) == VoidField; + } +} + +void +jumpAndInvoke(MyThread* t, object method, void* base, void* stack, ...) +{ + t->trace->targetMethod = 0; + + if (methodFlags(t, method) & ACC_NATIVE) { + t->trace->nativeMethod = method; + } else { + t->trace->nativeMethod = 0; + } + + unsigned argumentCount = methodParameterFootprint(t, method); + uintptr_t arguments[argumentCount]; + va_list a; va_start(a, stack); + for (unsigned i = 0; i < argumentCount; ++i) { + arguments[i] = va_arg(a, uintptr_t); + } + va_end(a); + + vmJumpAndInvoke + (t, reinterpret_cast(methodAddress(t, method)), + base, + stack, + argumentCount * BytesPerWord, + arguments, + (t->arch->alignFrameSize(t->arch->argumentFootprint(argumentCount)) + + t->arch->frameReturnAddressSize()) + * BytesPerWord); +} + +void +callContinuation(MyThread* t, object continuation, object result, + object exception) +{ + enum { + Call, + Unwind, + Rewind, + Throw + } action; + + object nextContinuation = 0; + + if (t->continuation == 0 + or continuationContext(t, t->continuation) + != continuationContext(t, continuation)) + { + PROTECT(t, continuation); + PROTECT(t, result); + PROTECT(t, exception); + + if (compatibleReturnType + (t, t->trace->originalMethod, continuationContextMethod + (t, continuationContext(t, continuation)))) + { + object oldContext; + object unwindContext; + + if (t->continuation) { + oldContext = continuationContext(t, t->continuation); + unwindContext = oldContext; + } else { + oldContext = 0; + unwindContext = 0; + } + + object rewindContext = 0; + + for (object newContext = continuationContext(t, continuation); + newContext; newContext = continuationContextNext(t, newContext)) + { + if (newContext == oldContext) { + unwindContext = 0; + break; + } else { + rewindContext = newContext; + } + } + + if (unwindContext + and continuationContextContinuation(t, unwindContext)) + { + nextContinuation = continuationContextContinuation(t, unwindContext); + result = makeUnwindResult(t, continuation, result, exception); + action = Unwind; + } else if (rewindContext + and continuationContextContinuation(t, rewindContext)) + { + nextContinuation = continuationContextContinuation(t, rewindContext); + action = Rewind; + + if (rewindMethod(t) == 0) { + PROTECT(t, nextContinuation); + + object method = resolveMethod + (t, "avian/Continuations", "rewind", + "(Ljava/lang/Runnable;Lavian/Callback;Ljava/lang/Object;" + "Ljava/lang/Throwable;)V"); + + if (method) { + rewindMethod(t) = method; + + compile(t, ::codeAllocator(t), 0, method); + + if (UNLIKELY(t->exception)) { + action = Throw; + } + } else { + action = Throw; + } + } + } else { + action = Call; + } + } else { + t->exception = makeIncompatibleContinuationException(t); + action = Throw; + } + } else { + action = Call; + } + + void* ip; + void* base; + void* stack; + findUnwindTarget(t, &ip, &base, &stack); + + switch (action) { + case Call: { + callContinuation(t, continuation, result, exception, ip, base, stack); + } break; + + case Unwind: { + callContinuation(t, nextContinuation, result, 0, ip, base, stack); + } break; + + case Rewind: { + t->continuation = nextContinuation; + + jumpAndInvoke + (t, rewindMethod(t), base, stack, + continuationContextBefore(t, continuationContext(t, nextContinuation)), + continuation, result, exception); + } break; + + case Throw: { + vmJump(ip, base, stack, t, 0, 0); + } break; + + default: + abort(t); + } +} + +void +callWithCurrentContinuation(MyThread* t, object receiver) +{ + object method = 0; + void* ip = 0; + void* base = 0; + void* stack = 0; + + { PROTECT(t, receiver); + + if (receiveMethod(t) == 0) { + object m = resolveMethod + (t, "avian/CallbackReceiver", "receive", + "(Lavian/Callback;)Ljava/lang/Object;"); + + if (m) { + receiveMethod(t) = m; + + object continuationClass = arrayBody + (t, t->m->types, Machine::ContinuationType); + + if (classVmFlags(t, continuationClass) & BootstrapFlag) { + resolveClass(t, vm::className(t, continuationClass)); + } + } + } + + if (LIKELY(t->exception == 0)) { + method = findInterfaceMethod + (t, receiveMethod(t), objectClass(t, receiver)); + PROTECT(t, method); + + compile(t, ::codeAllocator(t), 0, method); + + if (LIKELY(t->exception == 0)) { + t->continuation = makeCurrentContinuation(t, &ip, &base, &stack); + } + } + } + + if (LIKELY(t->exception == 0)) { + jumpAndInvoke(t, method, base, stack, receiver, t->continuation); + } else { + unwind(t); + } +} + +void +dynamicWind(MyThread* t, object before, object thunk, object after) +{ + void* ip = 0; + void* base = 0; + void* stack = 0; + + { PROTECT(t, before); + PROTECT(t, thunk); + PROTECT(t, after); + + if (windMethod(t) == 0) { + object method = resolveMethod + (t, "avian/Continuations", "wind", + "(Ljava/lang/Runnable;Ljava/util/concurrent/Callable;" + "Ljava/lang/Runnable;)Lavian/Continuations$UnwindResult;"); + + if (method) { + windMethod(t) = method; + compile(t, ::codeAllocator(t), 0, method); + } + } + + if (LIKELY(t->exception == 0)) { + t->continuation = makeCurrentContinuation(t, &ip, &base, &stack); + + object newContext = makeContinuationContext + (t, continuationContext(t, t->continuation), before, after, + t->continuation, t->trace->originalMethod); + + set(t, t->continuation, ContinuationContext, newContext); + } + } + + if (LIKELY(t->exception == 0)) { + jumpAndInvoke(t, windMethod(t), base, stack, before, thunk, after); + } else { + unwind(t); + } +} + class ArgumentList { public: ArgumentList(Thread* t, uintptr_t* array, unsigned size, bool* objectMask, @@ -4841,7 +6254,7 @@ class ArgumentList { array(array), objectMask(objectMask), size(size), - position(size), + position(0), protector(this) { if (this_) { @@ -4878,7 +6291,7 @@ class ArgumentList { array(array), objectMask(objectMask), size(size), - position(size), + position(0), protector(this) { if (this_) { @@ -4908,35 +6321,30 @@ class ArgumentList { } void addObject(object v) { - assert(t, position); + assert(t, position < size); - -- position; array[position] = reinterpret_cast(v); objectMask[position] = true; + ++ position; } void addInt(uintptr_t v) { - assert(t, position); + assert(t, position < size); - -- position; array[position] = v; objectMask[position] = false; + ++ position; } void addLong(uint64_t v) { - assert(t, position >= 2); + assert(t, position < size - 1); - position -= 2; - - if (BytesPerWord == 8) { - memcpy(array + position, &v, 8); - } else { - array[position] = v; - array[position + 1] = v >> 32; - } + memcpy(array + position, &v, 8); objectMask[position] = false; objectMask[position + 1] = false; + + position += 2; } MyThread* t; @@ -4950,7 +6358,7 @@ class ArgumentList { MyProtector(ArgumentList* list): Protector(list->t), list(list) { } virtual void visit(Heap::Visitor* v) { - for (unsigned i = list->position; i < list->size; ++i) { + for (unsigned i = 0; i < list->position; ++i) { if (list->objectMask[i]) { v->visit(reinterpret_cast(list->array + i)); } @@ -4971,19 +6379,17 @@ invoke(Thread* thread, object method, ArgumentList* arguments) uint64_t result; - { MyThread::CallTrace trace(t); + { MyThread::CallTrace trace(t, method); - if (methodFlags(t, method) & ACC_NATIVE) { - trace.nativeMethod = method; - } - - unsigned count = arguments->size - arguments->position; + assert(t, arguments->position == arguments->size); result = vmInvoke (t, reinterpret_cast(methodAddress(t, method)), - arguments->array + arguments->position, - count * BytesPerWord, - t->arch->alignFrameSize(count) * BytesPerWord, + arguments->array, + arguments->position * BytesPerWord, + t->arch->alignFrameSize + (t->arch->argumentFootprint(arguments->position)) + * BytesPerWord, returnType); } @@ -5061,6 +6467,11 @@ class SegFaultHandler: public System::SignalHandler { return true; } } + + if (compileLog) { + fflush(compileLog); + } + return false; } @@ -5073,39 +6484,18 @@ boot(MyThread* t, BootImage* image); class MyProcessor; void -compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, - BootImage* image, uint8_t* imageBase); +compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p); MyProcessor* processor(MyThread* t); class MyProcessor: public Processor { public: - class CodeAllocator: public Allocator { - public: - CodeAllocator(System* s): s(s) { } - - virtual void* tryAllocate(unsigned size) { - return s->tryAllocateExecutable(size); - } - - virtual void* allocate(unsigned size) { - void* p = tryAllocate(size); - expect(s, p); - return p; - } - - virtual void free(const void* p, unsigned size) { - s->freeExecutable(p, size); - } - - System* s; - }; - MyProcessor(System* s, Allocator* allocator): s(s), allocator(allocator), defaultThunk(0), + defaultVirtualThunk(0), nativeThunk(0), aioobThunk(0), callTable(0), @@ -5114,8 +6504,12 @@ class MyProcessor: public Processor { methodTreeSentinal(0), objectPools(0), staticTableArray(0), - codeAllocator(s), - codeZone(s, &codeAllocator, 64 * 1024) + virtualThunks(0), + receiveMethod(0), + windMethod(0), + rewindMethod(0), + codeAllocator(s, 0, 0), + bootImage(0) { } virtual Thread* @@ -5124,6 +6518,16 @@ class MyProcessor: public Processor { MyThread* t = new (m->heap->allocate(sizeof(MyThread))) MyThread(m, javaThread, static_cast(parent)); t->init(); + + if (false) { + fprintf(stderr, "%d\n", difference(&(t->continuation), t)); + fprintf(stderr, "%d\n", difference(&(t->exception), t)); + fprintf(stderr, "%d\n", difference(&(t->exceptionStackAdjustment), t)); + fprintf(stderr, "%d\n", difference(&(t->exceptionOffset), t)); + fprintf(stderr, "%d\n", difference(&(t->exceptionHandler), t)); + exit(0); + } + return t; } @@ -5172,28 +6576,12 @@ class MyProcessor: public Processor { virtual void initVtable(Thread* t, object c) - { - void* compiled = reinterpret_cast - (::defaultThunk(static_cast(t))); - - for (unsigned i = 0; i < classLength(t, c); ++i) { - classVtable(t, c, i) = compiled; - } - } - - virtual void - initClass(Thread* t, object c) { PROTECT(t, c); - - ACQUIRE(t, t->m->classLock); - if (classNeedsInit(t, c)) { - classVmFlags(t, c) |= InitFlag; - invoke(t, classInitializer(t, c), 0); - if (t->exception) { - t->exception = makeExceptionInInitializerError(t, t->exception); - } - classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag); + for (int i = classLength(t, c) - 1; i >= 0; --i) { + void* thunk = reinterpret_cast + (virtualThunk(static_cast(t), i)); + classVtable(t, c, i) = thunk; } } @@ -5208,12 +6596,21 @@ class MyProcessor: public Processor { v->visit(&methodTreeSentinal); v->visit(&objectPools); v->visit(&staticTableArray); + v->visit(&virtualThunks); + v->visit(&receiveMethod); + v->visit(&windMethod); + v->visit(&rewindMethod); } for (MyThread::CallTrace* trace = t->trace; trace; trace = trace->next) { + v->visit(&(trace->continuation)); v->visit(&(trace->nativeMethod)); + v->visit(&(trace->targetMethod)); + v->visit(&(trace->originalMethod)); } + v->visit(&(t->continuation)); + for (Reference* r = t->reference; r; r = r->next) { v->visit(&(r->target)); } @@ -5280,7 +6677,8 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), &codeZone, 0, method); + compile(static_cast(t), + ::codeAllocator(static_cast(t)), 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -5311,7 +6709,8 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), &codeZone, 0, method); + compile(static_cast(t), + ::codeAllocator(static_cast(t)), 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -5341,7 +6740,8 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), &codeZone, 0, method); + compile(static_cast(t), + ::codeAllocator(static_cast(t)), 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -5364,8 +6764,10 @@ class MyProcessor: public Processor { } virtual void dispose() { - codeZone.dispose(); - + if (codeAllocator.base) { + s->freeExecutable(codeAllocator.base, codeAllocator.capacity); + } + s->handleSegFault(0); allocator->free(this, sizeof(*this)); @@ -5429,46 +6831,38 @@ class MyProcessor: public Processor { collect(t, Heap::MinorCollection); } - return visitor.trace; + return visitor.trace ? visitor.trace : makeArray(t, 0); } - virtual void compileThunks(Thread* vmt, BootImage* image, uint8_t* code, - unsigned* offset, unsigned capacity) - { - MyThread* t = static_cast(vmt); - FixedAllocator allocator(t, code + *offset, capacity); - - ::compileThunks(t, &allocator, this, image, code); - - *offset += allocator.offset; + virtual void initialize(BootImage* image, uint8_t* code, unsigned capacity) { + bootImage = image; + codeAllocator.base = code; + codeAllocator.capacity = capacity; } - virtual void compileMethod(Thread* vmt, Zone* zone, uint8_t* code, - unsigned* offset, unsigned capacity, - object* constants, object* calls, - DelayedPromise** addresses, object method) + virtual void compileMethod(Thread* vmt, Zone* zone, object* constants, + object* calls, DelayedPromise** addresses, + object method) { MyThread* t = static_cast(vmt); - FixedAllocator allocator(t, code + *offset, capacity); BootContext bootContext(t, *constants, *calls, *addresses, zone); - compile(t, &allocator, &bootContext, method); + compile(t, &codeAllocator, &bootContext, method); *constants = bootContext.constants; *calls = bootContext.calls; *addresses = bootContext.addresses; - *offset += allocator.offset; } - virtual void visitRoots(BootImage* image, HeapWalker* w) { - image->methodTree = w->visitRoot(methodTree); - image->methodTreeSentinal = w->visitRoot(methodTreeSentinal); + virtual void visitRoots(HeapWalker* w) { + bootImage->methodTree = w->visitRoot(methodTree); + bootImage->methodTreeSentinal = w->visitRoot(methodTreeSentinal); + bootImage->virtualThunks = w->visitRoot(virtualThunks); } - virtual unsigned* makeCallTable(Thread* t, BootImage* image, HeapWalker* w, - uint8_t* code) - { - image->callCount = callTableSize; + virtual unsigned* makeCallTable(Thread* t, HeapWalker* w) { + bootImage->codeSize = codeAllocator.offset; + bootImage->callCount = callTableSize; unsigned* table = static_cast (t->m->heap->allocate(callTableSize * sizeof(unsigned) * 2)); @@ -5477,9 +6871,9 @@ class MyProcessor: public Processor { for (unsigned i = 0; i < arrayLength(t, callTable); ++i) { for (object p = arrayBody(t, callTable, i); p; p = callNodeNext(t, p)) { table[index++] = callNodeAddress(t, p) - - reinterpret_cast(code); + - reinterpret_cast(codeAllocator.base); table[index++] = w->map()->find(callNodeTarget(t, p)) - | (static_cast(callNodeVirtualCall(t, p)) << BootShift); + | (static_cast(callNodeFlags(t, p)) << BootShift); } } @@ -5487,6 +6881,12 @@ class MyProcessor: public Processor { } virtual void boot(Thread* t, BootImage* image) { + if (codeAllocator.base == 0) { + codeAllocator.base = static_cast + (s->tryAllocateExecutable(ExecutableAreaSizeInBytes)); + codeAllocator.capacity = ExecutableAreaSizeInBytes; + } + if (image) { ::boot(static_cast(t), image); } else { @@ -5496,17 +6896,66 @@ class MyProcessor: public Processor { set(t, methodTree, TreeNodeLeft, methodTreeSentinal); set(t, methodTree, TreeNodeRight, methodTreeSentinal); - ::compileThunks(static_cast(t), &codeZone, this, 0, 0); + ::compileThunks(static_cast(t), &codeAllocator, this); } segFaultHandler.m = t->m; expect(t, t->m->system->success (t->m->system->handleSegFault(&segFaultHandler))); } + + virtual void callWithCurrentContinuation(Thread* t, object receiver) { + if (Continuations) { + ::callWithCurrentContinuation(static_cast(t), receiver); + } else { + abort(t); + } + } + + virtual void dynamicWind(Thread* t, object before, object thunk, + object after) + { + if (Continuations) { + ::dynamicWind(static_cast(t), before, thunk, after); + } else { + abort(t); + } + } + + virtual void feedResultToContinuation(Thread* t, object continuation, + object result) + { + if (Continuations) { + callContinuation(static_cast(t), continuation, result, 0); + } else { + abort(t); + } + } + + virtual void feedExceptionToContinuation(Thread* t, object continuation, + object exception) + { + if (Continuations) { + callContinuation(static_cast(t), continuation, 0, exception); + } else { + abort(t); + } + } + + virtual void walkContinuationBody(Thread* t, Heap::Walker* w, object o, + unsigned start) + { + if (Continuations) { + ::walkContinuationBody(static_cast(t), w, o, start); + } else { + abort(t); + } + } System* s; Allocator* allocator; uint8_t* defaultThunk; + uint8_t* defaultVirtualThunk; uint8_t* nativeThunk; uint8_t* aioobThunk; uint8_t* thunkTable; @@ -5517,9 +6966,13 @@ class MyProcessor: public Processor { object methodTreeSentinal; object objectPools; object staticTableArray; + object virtualThunks; + object receiveMethod; + object windMethod; + object rewindMethod; SegFaultHandler segFaultHandler; - CodeAllocator codeAllocator; - Zone codeZone; + FixedAllocator codeAllocator; + BootImage* bootImage; }; object @@ -5577,7 +7030,7 @@ resizeTable(MyThread* t, object oldTable, unsigned newLength) object newNode = makeCallNode (t, callNodeAddress(t, oldNode), callNodeTarget(t, oldNode), - callNodeVirtualCall(t, oldNode), + callNodeFlags(t, oldNode), arrayBody(t, newTable, index)); set(t, newTable, ArrayBody + (index * BytesPerWord), newNode); @@ -5795,6 +7248,11 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code) updateCall(t, LongCall, false, code + image->compileMethodCall, voidPointer(::compileMethod)); + p->defaultVirtualThunk = code + image->defaultVirtualThunk; + + updateCall(t, LongCall, false, code + image->compileVirtualMethodCall, + voidPointer(::compileVirtualMethod)); + p->nativeThunk = code + image->nativeThunk; updateCall(t, LongCall, false, code + image->invokeNativeCall, @@ -5817,6 +7275,20 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code) #undef THUNK } +void +fixupVirtualThunks(MyThread* t, BootImage* image, uint8_t* code) +{ + MyProcessor* p = processor(t); + + for (unsigned i = 0; i < wordArrayLength(t, p->virtualThunks); ++i) { + if (wordArrayBody(t, p->virtualThunks, i)) { + wordArrayBody(t, p->virtualThunks, i) + = (wordArrayBody(t, p->virtualThunks, i) - image->codeBase) + + reinterpret_cast(code); + } + } +} + void boot(MyThread* t, BootImage* image) { @@ -5855,6 +7327,8 @@ boot(MyThread* t, BootImage* image) p->methodTree = bootObject(heap, image->methodTree); p->methodTreeSentinal = bootObject(heap, image->methodTreeSentinal); + p->virtualThunks = bootObject(heap, image->virtualThunks); + fixupCode(t, codeMap, codeMapSizeInWords, code, heap); syncInstructionCache(code, image->codeSize); @@ -5872,6 +7346,8 @@ boot(MyThread* t, BootImage* image) fixupThunks(t, image, code); + fixupVirtualThunks(t, image, code); + fixupMethods(t, image, code); t->m->bootstrapClassMap = makeHashMap(t, 0, 0); @@ -5887,8 +7363,7 @@ getThunk(MyThread* t, Thunk thunk) } void -compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, - BootImage* image, uint8_t* imageBase) +compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p) { class ThunkContext { public: @@ -5901,6 +7376,7 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, }; Zone zone(t->m->system, t->m->heap, 1024); + ThunkContext defaultContext(t, &zone); { Assembler* a = defaultContext.context.assembler; @@ -5921,10 +7397,52 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, a->endBlock(false)->resolve(0, 0); } + ThunkContext defaultVirtualContext(t, &zone); + + { Assembler* a = defaultVirtualContext.context.assembler; + + Assembler::Register class_(t->arch->virtualCallTarget()); + Assembler::Memory virtualCallTargetSrc + (t->arch->stack(), + (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) + * BytesPerWord); + + a->apply(Move, BytesPerWord, MemoryOperand, &virtualCallTargetSrc, + BytesPerWord, RegisterOperand, &class_); + + Assembler::Memory virtualCallTargetDst + (t->arch->thread(), difference(&(t->virtualCallTarget), t)); + + a->apply(Move, BytesPerWord, RegisterOperand, &class_, + BytesPerWord, MemoryOperand, &virtualCallTargetDst); + + Assembler::Register index(t->arch->virtualCallIndex()); + Assembler::Memory virtualCallIndex + (t->arch->thread(), difference(&(t->virtualCallIndex), t)); + + a->apply(Move, BytesPerWord, RegisterOperand, &index, + BytesPerWord, MemoryOperand, &virtualCallIndex); + + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); + + Assembler::Register thread(t->arch->thread()); + a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); + + Assembler::Constant proc(&(defaultVirtualContext.promise)); + a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); + + a->popFrame(); + + Assembler::Register result(t->arch->returnLow()); + a->apply(Jump, BytesPerWord, RegisterOperand, &result); + + a->endBlock(false)->resolve(0, 0); + } + ThunkContext nativeContext(t, &zone); { Assembler* a = nativeContext.context.assembler; - + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); Assembler::Register thread(t->arch->thread()); @@ -5933,9 +7451,7 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, Assembler::Constant proc(&(nativeContext.promise)); a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); - a->popFrame(); - - a->apply(Return); + a->popFrameAndUpdateStackAndReturn(difference(&(t->stack), t)); a->endBlock(false)->resolve(0, 0); } @@ -5969,19 +7485,12 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, p->thunkSize = pad(tableContext.context.assembler->length()); - expect(t, codeZone(t)->ensure - (codeSingletonSizeInBytes - (t, defaultContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, nativeContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, aioobContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, p->thunkSize * ThunkCount))); - p->defaultThunk = finish (t, allocator, defaultContext.context.assembler, "default"); + BootImage* image = p->bootImage; + uint8_t* imageBase = p->codeAllocator.base; + { void* call; defaultContext.promise.listener->resolve (reinterpret_cast(voidPointer(compileMethod)), &call); @@ -5992,6 +7501,20 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, } } + p->defaultVirtualThunk = finish + (t, allocator, defaultVirtualContext.context.assembler, "defaultVirtual"); + + { void* call; + defaultVirtualContext.promise.listener->resolve + (reinterpret_cast(voidPointer(compileVirtualMethod)), &call); + + if (image) { + image->defaultVirtualThunk = p->defaultVirtualThunk - imageBase; + image->compileVirtualMethodCall + = static_cast(call) - imageBase; + } + } + p->nativeThunk = finish (t, allocator, nativeContext.context.assembler, "native"); @@ -6060,12 +7583,36 @@ objectPools(MyThread* t) return processor(t)->objectPools; } +object& +windMethod(MyThread* t) +{ + return processor(t)->windMethod; +} + +object& +rewindMethod(MyThread* t) +{ + return processor(t)->rewindMethod; +} + +object& +receiveMethod(MyThread* t) +{ + return processor(t)->receiveMethod; +} + uintptr_t defaultThunk(MyThread* t) { return reinterpret_cast(processor(t)->defaultThunk); } +uintptr_t +defaultVirtualThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->defaultVirtualThunk); +} + uintptr_t nativeThunk(MyThread* t) { @@ -6078,6 +7625,61 @@ aioobThunk(MyThread* t) return reinterpret_cast(processor(t)->aioobThunk); } +uintptr_t +compileVirtualThunk(MyThread* t, unsigned index) +{ + Context context(t); + Assembler* a = context.assembler; + + ResolvedPromise indexPromise(index); + Assembler::Constant indexConstant(&indexPromise); + Assembler::Register indexRegister(t->arch->virtualCallIndex()); + a->apply(Move, BytesPerWord, ConstantOperand, &indexConstant, + BytesPerWord, RegisterOperand, &indexRegister); + + ResolvedPromise defaultVirtualThunkPromise(defaultVirtualThunk(t)); + Assembler::Constant thunk(&defaultVirtualThunkPromise); + a->apply(Jump, BytesPerWord, ConstantOperand, &thunk); + + a->endBlock(false)->resolve(0, 0); + + uint8_t* start = static_cast + (codeAllocator(t)->allocate(a->length())); + + a->writeTo(start); + + logCompile(t, start, a->length(), 0, "virtualThunk", 0); + + return reinterpret_cast(start); +} + +uintptr_t +virtualThunk(MyThread* t, unsigned index) +{ + MyProcessor* p = processor(t); + + if (p->virtualThunks == 0 or wordArrayLength(t, p->virtualThunks) <= index) { + object newArray = makeWordArray(t, nextPowerOfTwo(index + 1)); + if (p->virtualThunks) { + memcpy(&wordArrayBody(t, newArray, 0), + &wordArrayBody(t, p->virtualThunks, 0), + wordArrayLength(t, p->virtualThunks) * BytesPerWord); + } + p->virtualThunks = newArray; + } + + if (wordArrayBody(t, p->virtualThunks, index) == 0) { + ACQUIRE(t, t->m->classLock); + + if (wordArrayBody(t, p->virtualThunks, index) == 0) { + uintptr_t thunk = compileVirtualThunk(t, index); + wordArrayBody(t, p->virtualThunks, index) = thunk; + } + } + + return wordArrayBody(t, p->virtualThunks, index); +} + void compile(MyThread* t, Allocator* allocator, BootContext* bootContext, object method) @@ -6159,9 +7761,10 @@ methodTreeSentinal(MyThread* t) return processor(t)->methodTreeSentinal; } -Zone* -codeZone(MyThread* t) { - return &(processor(t)->codeZone); +FixedAllocator* +codeAllocator(MyThread* t) +{ + return &(processor(t)->codeAllocator); } } // namespace diff --git a/src/compiler.cpp b/src/compiler.cpp index 6ef7751355..52d3dbfa99 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -7,9 +7,9 @@ There is NO WARRANTY for this software. See license.txt for details. */ - #include "compiler.h" #include "assembler.h" +#include "machine.h" using namespace vm; @@ -138,7 +138,7 @@ class SitePair { Site* high; }; -class Stack: public Compiler::StackElement { +class Stack { public: Stack(unsigned index, Value* value, Stack* next): index(index), value(value), next(next) @@ -257,6 +257,8 @@ class Read { { } virtual bool intersect(SiteMask* mask) = 0; + + virtual Value* successor() = 0; virtual bool valid() = 0; @@ -293,9 +295,9 @@ enum ValueType { class Value: public Compiler::Operand { public: - Value(Site* site, Site* target): + Value(Site* site, Site* target, ValueType type): reads(0), lastRead(0), sites(site), source(0), target(target), buddy(this), - high(0), home(NoFrameIndex), type(ValueGeneral) + high(0), home(NoFrameIndex), type(type) { } virtual void addPredecessor(Context*, Event*) { } @@ -397,13 +399,15 @@ unsigned RegisterResource::toString(Context* c, char* buffer, unsigned bufferSize) { return snprintf - (buffer, bufferSize, "register %"LD, this - c->registerResources); + (buffer, bufferSize, "register %d", static_cast + (this - c->registerResources)); } unsigned FrameResource::toString(Context* c, char* buffer, unsigned bufferSize) { - return snprintf(buffer, bufferSize, "frame %"LD, this - c->frameResources); + return snprintf(buffer, bufferSize, "frame %d", static_cast + (this - c->frameResources)); } class PoolPromise: public Promise { @@ -633,6 +637,8 @@ class Event { virtual bool isBranch() { return false; } + virtual bool allExits() { return false; } + Event* next; Stack* stackBefore; Local* localsBefore; @@ -651,42 +657,58 @@ class Event { }; unsigned -usableFrameSize(Context* c) +totalFrameSize(Context* c) { - return c->alignedFrameSize - c->arch->frameFooterSize(); + return c->alignedFrameSize + + c->arch->frameHeaderSize() + + c->arch->argumentFootprint(c->parameterFootprint); } int -frameIndex(Context* c, int index) +frameIndex(Context* c, int localIndex) { - assert(c, static_cast - (usableFrameSize(c) + c->parameterFootprint - index - 1) >= 0); + assert(c, localIndex >= 0); - return usableFrameSize(c) + c->parameterFootprint - index - 1; + int index = c->alignedFrameSize + c->parameterFootprint - localIndex - 1; + + if (localIndex < static_cast(c->parameterFootprint)) { + index += c->arch->frameHeaderSize(); + } else { + index -= c->arch->frameFooterSize(); + } + + assert(c, index >= 0); + assert(c, static_cast(index) < totalFrameSize(c)); + + return index; } unsigned frameIndexToOffset(Context* c, unsigned frameIndex) { - return ((frameIndex >= usableFrameSize(c)) ? - (frameIndex - + (c->arch->frameFooterSize() * 2) - + c->arch->frameHeaderSize()) : - (frameIndex - + c->arch->frameFooterSize())) * BytesPerWord; + assert(c, frameIndex < totalFrameSize(c)); + + return (frameIndex + c->arch->frameFooterSize()) * BytesPerWord; } unsigned offsetToFrameIndex(Context* c, unsigned offset) { - unsigned normalizedOffset = offset / BytesPerWord; + assert(c, static_cast + ((offset / BytesPerWord) - c->arch->frameFooterSize()) >= 0); + assert(c, ((offset / BytesPerWord) - c->arch->frameFooterSize()) + < totalFrameSize(c)); - return ((normalizedOffset >= c->alignedFrameSize) ? - (normalizedOffset - - (c->arch->frameFooterSize() * 2) - - c->arch->frameHeaderSize()) : - (normalizedOffset - - c->arch->frameFooterSize())); + return (offset / BytesPerWord) - c->arch->frameFooterSize(); +} + +unsigned +frameBase(Context* c) +{ + return c->alignedFrameSize + - c->arch->frameReturnAddressSize() + - c->arch->frameFooterSize() + + c->arch->frameHeaderSize(); } class FrameIterator { @@ -1105,7 +1127,11 @@ FrameResource::thaw(Context* c, Value* v) class Target { public: - static const unsigned Penalty = 10; + static const unsigned MinimumRegisterCost = 0; + static const unsigned MinimumFrameCost = 1; + static const unsigned StealPenalty = 2; + static const unsigned StealUniquePenalty = 4; + static const unsigned LowRegisterPenalty = 10; static const unsigned Impossible = 20; Target(): cost(Impossible) { } @@ -1119,6 +1145,27 @@ class Target { uint8_t cost; }; +ValueType +resultType(unsigned code) { + switch (code) { + case VoidField: + case ByteField: + case BooleanField: + case CharField: + case ShortField: + case ObjectField: + case LongField: + case IntField: + return ValueGeneral; + case FloatField: + case DoubleField: + return ValueFloat; + default: + //abort(t); + return ValueGeneral; + } +} + Target pickTarget(Context* c, Read* r, bool intersectRead, unsigned registerReserveCount); @@ -1134,9 +1181,9 @@ resourceCost(Context* c UNUSED, Value* v, Resource* r) if (v and buddies(r->value, v)) { return 0; } else if (hasMoreThanOneSite(r->value)) { - return 2; + return Target::StealPenalty; } else { - return 4; + return Target::StealUniquePenalty; } } else { return 0; @@ -1158,7 +1205,7 @@ pickRegisterTarget(Context* c, Value* v, uint32_t mask, unsigned* cost) for (int i = c->arch->registerCount() - 1; i >= 0; --i) { if ((1 << i) & mask) { RegisterResource* r = c->registerResources + i; - unsigned myCost = resourceCost(c, v, r); + unsigned myCost = resourceCost(c, v, r) + Target::MinimumRegisterCost; if ((static_cast(1) << i) == mask) { *cost = myCost; return i; @@ -1184,7 +1231,8 @@ pickRegisterTarget(Context* c, Value* v, uint32_t mask) unsigned frameCost(Context* c, Value* v, int frameIndex) { - return resourceCost(c, v, c->frameResources + frameIndex) + 1; + return resourceCost(c, v, c->frameResources + frameIndex) + + Target::MinimumFrameCost; } Target @@ -1196,7 +1244,7 @@ pickFrameTarget(Context* c, Value* v) do { if (p->home >= 0) { Target mine(p->home, MemoryOperand, frameCost(c, v, p->home)); - if (mine.cost == 1) { + if (mine.cost == Target::MinimumFrameCost) { return mine; } else if (mine.cost < best.cost) { best = mine; @@ -1213,10 +1261,10 @@ pickAnyFrameTarget(Context* c, Value* v) { Target best; - unsigned count = usableFrameSize(c) + c->parameterFootprint; + unsigned count = totalFrameSize(c); for (unsigned i = 0; i < count; ++i) { Target mine(i, MemoryOperand, frameCost(c, v, i)); - if (mine.cost == 1) { + if (mine.cost == Target::MinimumFrameCost) { return mine; } else if (mine.cost < best.cost) { best = mine; @@ -1227,37 +1275,14 @@ pickAnyFrameTarget(Context* c, Value* v) } Target -pickTarget(Context* c, Read* read, bool intersectRead, - unsigned registerReserveCount) +pickTarget(Context* c, Value* value, const SiteMask& mask, + unsigned registerPenalty, Target best) { - SiteMask mask; - read->intersect(&mask); - - unsigned registerPenalty; - if(read->value) { - if(read->value->type == ValueGeneral) { - registerPenalty = (c->generalRegisterCount > registerReserveCount - ? 0 : Target::Penalty); - } else if(read->value->type == ValueFloat) { - registerPenalty = (c->floatRegisterCount > registerReserveCount - ? 0 : Target::Penalty); - } else { - registerPenalty = (c->availableRegisterCount > registerReserveCount - ? 0 : Target::Penalty); - } - } else { - registerPenalty = (c->availableRegisterCount > registerReserveCount - ? 0 : Target::Penalty); - } - - - Target best; - if ((mask.typeMask & (1 << RegisterOperand))) { - Target mine = pickRegisterTarget(c, read->value, mask.registerMask); + if (mask.typeMask & (1 << RegisterOperand)) { + Target mine = pickRegisterTarget(c, value, mask.registerMask); mine.cost += registerPenalty; - if(mine.cost == Target::Impossible) asm("int3"); - if (mine.cost == 0) { + if (mine.cost == Target::MinimumRegisterCost) { return mine; } else if (mine.cost < best.cost) { best = mine; @@ -1266,14 +1291,64 @@ pickTarget(Context* c, Read* read, bool intersectRead, if ((mask.typeMask & (1 << MemoryOperand)) && mask.frameIndex >= 0) { Target mine(mask.frameIndex, MemoryOperand, - frameCost(c, read->value, mask.frameIndex)); - if (mine.cost == 0) { + frameCost(c, value, mask.frameIndex)); + if (mine.cost == Target::MinimumFrameCost) { return mine; } else if (mine.cost < best.cost) { best = mine; } } + return best; +} + +Target +pickTarget(Context* c, Read* read, bool intersectRead, + unsigned registerReserveCount) +{ + /*unsigned registerPenalty = (c->availableRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty);*/ + unsigned registerPenalty; + if(read->value) { + if(read->value->type == ValueGeneral) { + registerPenalty = (c->generalRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty); + } else if(read->value->type == ValueFloat) { + registerPenalty = (c->floatRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty); + } else { + registerPenalty = (c->availableRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty); + } + } else { + registerPenalty = (c->availableRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty); + } + + SiteMask mask; + read->intersect(&mask); + + Target best; + + Value* successor = read->successor(); + if (successor) { + Read* r = live(successor); + if (r) { + SiteMask intersection = mask; + if (r->intersect(&intersection)) { + best = pickTarget(c, read->value, intersection, registerPenalty, best); + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + } + } + } + + best = pickTarget(c, read->value, mask, registerPenalty, best); + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + if (intersectRead) { return best; } @@ -1282,7 +1357,7 @@ pickTarget(Context* c, Read* read, bool intersectRead, mine.cost += registerPenalty; - if (mine.cost == 0) { + if (mine.cost == Target::MinimumRegisterCost) { return mine; } else if (mine.cost < best.cost) { best = mine; @@ -1290,14 +1365,16 @@ pickTarget(Context* c, Read* read, bool intersectRead, } { Target mine = pickFrameTarget(c, read->value); - if (mine.cost == 0) { + if (mine.cost == Target::MinimumFrameCost) { return mine; } else if (mine.cost < best.cost) { best = mine; } } - if (best.cost > 3 and c->availableRegisterCount == 0) { + if (best.cost >= Target::StealUniquePenalty + and c->availableRegisterCount == 0) + { // there are no free registers left, so moving from memory to // memory isn't an option - try harder to find an available frame // site: @@ -1305,6 +1382,7 @@ pickTarget(Context* c, Read* read, bool intersectRead, assert(c, best.cost <= 3); } + //if(best.cost == Target::Impossible)asm("int3"); return best; } @@ -1937,8 +2015,8 @@ release(Context* c, Resource* resource, Value* value UNUSED, Site* site UNUSED) class SingleRead: public Read { public: - SingleRead(const SiteMask& mask): - next_(0), mask(mask) + SingleRead(const SiteMask& mask, Value* successor): + next_(0), mask(mask), successor_(successor) { } virtual bool intersect(SiteMask* mask) { @@ -1946,6 +2024,10 @@ class SingleRead: public Read { return true; } + + virtual Value* successor() { + return successor_; + } virtual bool valid() { return true; @@ -1962,15 +2044,16 @@ class SingleRead: public Read { Read* next_; SiteMask mask; + Value* successor_; }; Read* -read(Context* c, const SiteMask& mask) +read(Context* c, const SiteMask& mask, Value* successor = 0) { assert(c, (mask.typeMask != 1 << MemoryOperand) or mask.frameIndex >= 0); return new (c->zone->allocate(sizeof(SingleRead))) - SingleRead(mask); + SingleRead(mask, successor); } Read* @@ -2018,6 +2101,10 @@ class MultiRead: public Read { return result; } + virtual Value* successor() { + return 0; + } + virtual bool valid() { bool result = false; if (not visited) { @@ -2068,7 +2155,7 @@ class MultiRead: public Read { } Read* nextTarget() { -// fprintf(stderr, "next target for %p: %p\n", this, firstTarget); + // fprintf(stderr, "next target for %p: %p\n", this, firstTarget); Read* r = static_cast(firstTarget->value); firstTarget = firstTarget->next; @@ -2108,6 +2195,10 @@ class StubRead: public Read { return valid_; } + virtual Value* successor() { + return 0; + } + virtual bool valid() { return valid_; } @@ -2207,7 +2298,7 @@ addRead(Context* c, Event* e, Value* v, Read* r) if (DebugReads) { fprintf(stderr, "add read %p to %p last %p event %p (%s)\n", r, v, v->lastRead, e, (e ? e->name() : 0)); } - +//if(!e)asm("int3"); r->value = v; if (e) { r->event = e; @@ -2217,9 +2308,9 @@ addRead(Context* c, Event* e, Value* v, Read* r) } if (v->lastRead) { -// if (DebugReads) { -// fprintf(stderr, "append %p to %p for %p\n", r, v->lastRead, v); -// } + // if (DebugReads) { + // fprintf(stderr, "append %p to %p for %p\n", r, v->lastRead, v); + // } v->lastRead->append(c, r); } else { @@ -2238,11 +2329,14 @@ clean(Context* c, Value* v, unsigned popIndex) (c, static_cast(s)->offset) >= popIndex)) { - if (false) { + if (false and + s->match(c, SiteMask(1 << MemoryOperand, 0, AnyFrameIndex))) + { char buffer[256]; s->toString(c, buffer, 256); - fprintf(stderr, "remove %s from %p at %d pop index %d\n", + fprintf(stderr, "remove %s from %p at %d pop offset 0x%x\n", buffer, v, offsetToFrameIndex - (c, static_cast(s)->offset), popIndex); + (c, static_cast(s)->offset), + frameIndexToOffset(c, popIndex)); } it.remove(c); } @@ -2287,8 +2381,7 @@ saveLocals(Context* c, Event* e) if (local->value) { if (DebugReads) { fprintf(stderr, "local save read %p at %d of %d\n", - local->value, ::frameIndex(c, li), - usableFrameSize(c) + c->parameterFootprint); + local->value, ::frameIndex(c, li), totalFrameSize(c)); } addRead(c, e, local->value, read @@ -2307,18 +2400,24 @@ class CallEvent: public Event { address(address), traceHandler(traceHandler), result(result), + returnAddressSurrogate(0), + framePointerSurrogate(0), popIndex(0), - padIndex(0), - padding(0), + stackArgumentIndex(0), flags(flags), - resultSize(resultSize) + resultSize(resultSize), + stackArgumentFootprint(stackArgumentFootprint) { - uint32_t registerMask = ~0; - Stack* s = argumentStack; - unsigned index = 0; - unsigned frameIndex = 0; + uint32_t registerMask = c->arch->allRegisters(); if (argumentCount) { + assert(c, (flags & Compiler::TailJump) == 0); + assert(c, stackArgumentFootprint == 0); + + Stack* s = argumentStack; + unsigned frameIndex = 0; + unsigned index = 0; + while (true) { Read* target; if (index < c->arch->argumentRegisterCount()) { @@ -2338,6 +2437,7 @@ class CallEvent: public Event { target = read(c, SiteMask(1 << MemoryOperand, 0, frameIndex)); ++ frameIndex; } + addRead(c, this, s->value, target); if ((++ index) < argumentCount) { @@ -2359,63 +2459,95 @@ class CallEvent: public Event { ((flags & Compiler::Aligned) ? AlignedCall : Call, BytesPerWord, &typeMask, &planRegisterMask, &thunk); - assert(c, thunk == 0); + assert(c, not thunk); addRead(c, this, address, read (c, SiteMask (typeMask, registerMask & planRegisterMask, AnyFrameIndex))); } - int footprint = stackArgumentFootprint; - for (Stack* s = stackBefore; s; s = s->next) { - if (s->value) { - if (footprint > 0) { + Stack* stack = stackBefore; + + if (stackArgumentFootprint) { + int footprint = stackArgumentFootprint; + int returnAddressIndex; + int framePointerIndex; + int frameOffset; + + if (TailCalls and (flags & Compiler::TailJump)) { + assert(c, argumentCount == 0); + + int base = frameBase(c); + returnAddressIndex = base + c->arch->returnAddressOffset(); + framePointerIndex = base + c->arch->framePointerOffset(); + + frameOffset = totalFrameSize(c) + - c->arch->argumentFootprint(stackArgumentFootprint) - 1; + } else { + returnAddressIndex = -1; + framePointerIndex = -1; + frameOffset = -1; + } + + while (footprint > 0) { + if (stack->value) { + int frameIndex = footprint + frameOffset; + if (DebugReads) { fprintf(stderr, "stack arg read %p at %d of %d\n", - s->value, frameIndex, - usableFrameSize(c) + c->parameterFootprint); + stack->value, frameIndex, totalFrameSize(c)); } - addRead(c, this, s->value, read - (c, SiteMask(1 << MemoryOperand, 0, frameIndex))); - } else { + if (static_cast(frameIndex) == returnAddressIndex) { + returnAddressSurrogate = stack->value; + addRead(c, this, stack->value, generalRegisterRead(c)); + } else if (static_cast(frameIndex) == framePointerIndex) { + framePointerSurrogate = stack->value; + addRead(c, this, stack->value, generalRegisterRead(c)); + } else { + addRead(c, this, stack->value, read + (c, SiteMask(1 << MemoryOperand, 0, frameIndex))); + } + } + + stack = stack->next; + -- footprint; + } + } + + if ((not TailCalls) or (flags & Compiler::TailJump) == 0) { + stackArgumentIndex = c->localFootprint; + if (stackBefore) { + stackArgumentIndex += stackBefore->index + 1 - stackArgumentFootprint; + } + + popIndex + = c->alignedFrameSize + + c->parameterFootprint + - c->arch->frameFooterSize() + - stackArgumentIndex; + + assert(c, static_cast(popIndex) >= 0); + + while (stack) { + if (stack->value) { unsigned logicalIndex = ::frameIndex - (c, s->index + c->localFootprint); + (c, stack->index + c->localFootprint); if (DebugReads) { fprintf(stderr, "stack save read %p at %d of %d\n", - s->value, logicalIndex, - usableFrameSize(c) + c->parameterFootprint); + stack->value, logicalIndex, totalFrameSize(c)); } - addRead(c, this, s->value, read + addRead(c, this, stack->value, read (c, SiteMask(1 << MemoryOperand, 0, logicalIndex))); } + + stack = stack->next; } - -- footprint; - - if (footprint == 0) { - unsigned logicalIndex = ::frameIndex(c, s->index + c->localFootprint); - - assert(c, logicalIndex >= frameIndex); - - padding = logicalIndex - frameIndex; - padIndex = s->index + c->localFootprint; - } - - ++ frameIndex; + saveLocals(c, this); } - - popIndex - = usableFrameSize(c) - + c->parameterFootprint - - (stackBefore ? stackBefore->index + 1 - stackArgumentFootprint : 0) - - c->localFootprint; - - assert(c, static_cast(popIndex) >= 0); - - saveLocals(c, this); } virtual const char* name() { @@ -2423,12 +2555,76 @@ class CallEvent: public Event { } virtual void compile(Context* c) { - apply(c, (flags & Compiler::Aligned) ? AlignedCall : Call, BytesPerWord, - address->source, 0); + UnaryOperation op; + + if (TailCalls and (flags & Compiler::TailJump)) { + if (flags & Compiler::Aligned) { + op = AlignedJump; + } else { + op = Jump; + } + + assert(c, returnAddressSurrogate == 0 + or returnAddressSurrogate->source->type(c) == RegisterOperand); + assert(c, framePointerSurrogate == 0 + or framePointerSurrogate->source->type(c) == RegisterOperand); + + int ras; + if (returnAddressSurrogate) { + returnAddressSurrogate->source->freeze(c, returnAddressSurrogate); + + ras = static_cast + (returnAddressSurrogate->source)->number; + } else { + ras = NoRegister; + } + + int fps; + if (framePointerSurrogate) { + framePointerSurrogate->source->freeze(c, framePointerSurrogate); + + fps = static_cast + (framePointerSurrogate->source)->number; + } else { + fps = NoRegister; + } + + int offset + = static_cast(c->arch->argumentFootprint(stackArgumentFootprint)) + - static_cast(c->arch->argumentFootprint(c->parameterFootprint)); + + c->assembler->popFrameForTailCall(c->alignedFrameSize, offset, ras, fps); + } else if (flags & Compiler::Aligned) { + op = AlignedCall; + } else { + op = Call; + } + + apply(c, op, BytesPerWord, address->source, 0); if (traceHandler) { traceHandler->handleTrace(codePromise(c, c->assembler->offset()), - padIndex, padding); + stackArgumentIndex); + } + + if (TailCalls) { + if (flags & Compiler::TailJump) { + if (returnAddressSurrogate) { + returnAddressSurrogate->source->thaw(c, returnAddressSurrogate); + } + + if (framePointerSurrogate) { + framePointerSurrogate->source->thaw(c, framePointerSurrogate); + } + } else { + unsigned footprint = c->arch->argumentFootprint + (stackArgumentFootprint); + + if (footprint > c->arch->stackAlignmentInWords()) { + c->assembler->adjustFrame + (footprint - c->arch->stackAlignmentInWords()); + } + } } clean(c, this, stackBefore, localsBefore, reads, popIndex); @@ -2441,14 +2637,20 @@ class CallEvent: public Event { } } + virtual bool allExits() { + return (flags & Compiler::TailJump) != 0; + } + Value* address; TraceHandler* traceHandler; Value* result; + Value* returnAddressSurrogate; + Value* framePointerSurrogate; unsigned popIndex; - unsigned padIndex; - unsigned padding; + unsigned stackArgumentIndex; unsigned flags; unsigned resultSize; + unsigned stackArgumentFootprint; }; void @@ -2463,6 +2665,15 @@ appendCall(Context* c, Value* address, unsigned flags, stackArgumentFootprint)); } +bool +unreachable(Event* event) +{ + for (Link* p = event->predecessors; p; p = p->nextPredecessor) { + if (not p->predecessor->allExits()) return false; + } + return event->predecessors != 0; +} + class ReturnEvent: public Event { public: ReturnEvent(Context* c, unsigned size, Value* value): @@ -2486,8 +2697,10 @@ class ReturnEvent: public Event { popRead(c, this, r->value); } - c->assembler->popFrame(); - c->assembler->apply(Return); + if (not unreachable(this)) { + c->assembler->popFrameAndPopArgumentsAndReturn + (c->arch->argumentFootprint(c->parameterFootprint)); + } } Value* value; @@ -2507,6 +2720,7 @@ addBuddy(Value* original, Value* buddy) Value* p = original; while (p->buddy != original) p = p->buddy; p->buddy = buddy; + //buddy->type = original->type; if (DebugBuddies) { fprintf(stderr, "add buddy %p to", buddy); @@ -2655,9 +2869,9 @@ maybeMove(Context* c, BinaryOperation type, unsigned srcSize, } Value* -value(Context* c, Site* site = 0, Site* target = 0) +value(Context* c, ValueType type, Site* site = 0, Site* target = 0) { - return new (c->zone->allocate(sizeof(Value))) Value(site, target); + return new (c->zone->allocate(sizeof(Value))) Value(site, target, type); } void @@ -2665,7 +2879,7 @@ split(Context* c, Value* v) { assert(c, v->high == 0); - v->high = value(c); + v->high = value(c, v->type); for (SiteIterator it(v); it.hasMore();) { Site* s = it.next(); removeSite(c, v, s); @@ -2688,7 +2902,7 @@ grow(Context* c, Value* v) { assert(c, v->high == 0); - v->high = value(c); + v->high = value(c, v->type); } class MoveEvent: public Event { @@ -2703,15 +2917,19 @@ class MoveEvent: public Event { { assert(c, srcSelectSize <= srcSize); - addRead(c, this, src, read(c, srcLowMask)); - if (srcSelectSize > BytesPerWord) { - maybeSplit(c, src); - addRead(c, this, src->high, read(c, srcHighMask)); - } + bool noop = srcSelectSize >= dstSize; if (dstSize > BytesPerWord) { grow(c, dst); } + + addRead(c, this, src, read(c, srcLowMask, noop ? dst : 0)); + if (srcSelectSize > BytesPerWord) { + maybeSplit(c, src); + addRead(c, this, src->high, read + (c, srcHighMask, + noop and dstSize > BytesPerWord ? dst->high : 0)); + } } virtual const char* name() { @@ -2928,14 +3146,14 @@ getTarget(Context* c, Value* value, Value* result, const SiteMask& resultMask) Site* s; Value* v; Read* r = liveNext(c, value); - if (r == 0 and value->source->match(c, static_cast(resultMask))) { + if (value->source->match(c, static_cast(resultMask))) { s = value->source; v = value; if (r and not hasMoreThanOneSite(v)) { preserve(c, v, s, r); } } else { - SingleRead r(resultMask); + SingleRead r(resultMask, 0); s = pickTargetSite(c, &r, true); v = result; addSite(c, result, s); @@ -2998,14 +3216,17 @@ class CombineEvent: public Event { addRead(c, this, first->high, read(c, firstHighMask)); } - addRead(c, this, second, read(c, secondLowMask)); - if (secondSize > BytesPerWord) { - addRead(c, this, second->high, read(c, secondHighMask)); - } - if (resultSize > BytesPerWord) { grow(c, result); } + + bool condensed = c->arch->alwaysCondensed(type); + + addRead(c, this, second, read(c, secondLowMask, condensed ? result : 0)); + if (secondSize > BytesPerWord) { + addRead(c, this, second->high, read + (c, secondHighMask, condensed ? result->high : 0)); + } } virtual const char* name() { @@ -3032,7 +3253,7 @@ class CombineEvent: public Event { ? getTarget(c, second->high, result->high, resultHighMask) : 0); -// fprintf(stderr, "combine %p and %p into %p\n", first, second, result); + // fprintf(stderr, "combine %p and %p into %p\n", first, second, result); apply(c, type, firstSize, first->source, source(first->high), secondSize, second->source, source(second->high), resultSize, low, high); @@ -3176,15 +3397,15 @@ pushWord(Context* c, Value* v) } Value* -push(Context* c, unsigned footprint, Value* v) +push(Context* c, unsigned footprint, Value* v, bool reverse) { assert(c, footprint); - bool bigEndian = c->arch->bigEndian(); + bool lowFirst = reverse xor c->arch->bigEndian(); Value* low = v; - if (bigEndian) { + if (lowFirst) { v = pushWord(c, v); } @@ -3203,7 +3424,7 @@ push(Context* c, unsigned footprint, Value* v) high = 0; } - if (not bigEndian) { + if (not lowFirst) { v = pushWord(c, v); } @@ -3236,7 +3457,7 @@ pop(Context* c, unsigned footprint) bool bigEndian = c->arch->bigEndian(); - if (not bigEndian) { + if (bigEndian) { s = c->stack; } @@ -3247,11 +3468,11 @@ pop(Context* c, unsigned footprint) Stack* low; Stack* high; if (bigEndian) { - high = c->stack; - low = high->next; - } else { low = c->stack; high = low->next; + } else { + high = c->stack; + low = high->next; } assert(c, low->value->high == high->value @@ -3261,7 +3482,7 @@ pop(Context* c, unsigned footprint) popWord(c); } - if (bigEndian) { + if (not bigEndian) { s = c->stack; } @@ -3366,14 +3587,14 @@ appendCombine(Context* c, TernaryOperation type, if (thunk) { Stack* oldStack = c->stack; - ::push(c, ceiling(secondSize, BytesPerWord), second); - ::push(c, ceiling(firstSize, BytesPerWord), first); + ::push(c, ceiling(secondSize, BytesPerWord), second, false); + ::push(c, ceiling(firstSize, BytesPerWord), first, false); Stack* argumentStack = c->stack; c->stack = oldStack; appendCall - (c, value(c, constantSite(c, c->client->getThunk(type, firstSize, resultSize))), + (c, value(c, ValueGeneral, constantSite(c, c->client->getThunk(type, firstSize, resultSize))), 0, 0, result, resultSize, argumentStack, ceiling(secondSize, BytesPerWord) + ceiling(firstSize, BytesPerWord), 0); @@ -3400,10 +3621,13 @@ class TranslateEvent: public Event { const SiteMask& valueHighMask): Event(c), type(type), size(size), resSize(resSize), value(value), result(result) { - addRead(c, this, value, read(c, valueLowMask)); + bool condensed = c->arch->alwaysCondensed(type); + + addRead(c, this, value, read(c, valueLowMask, condensed ? result : 0)); if (size > BytesPerWord) { - addRead(c, this, value->high, read(c, valueHighMask)); grow(c, result); + addRead(c, this, value->high, read + (c, valueHighMask, condensed ? result->high : 0)); } } @@ -3472,13 +3696,13 @@ appendTranslate(Context* c, BinaryOperation type, unsigned firstSize, Value* fir if (thunk) { Stack* oldStack = c->stack; - ::push(c, ceiling(firstSize, BytesPerWord), first); + ::push(c, ceiling(firstSize, BytesPerWord), first, false); Stack* argumentStack = c->stack; c->stack = oldStack; appendCall - (c, value(c, constantSite(c, c->client->getThunk(type, firstSize, resultSize))), + (c, value(c, ValueGeneral, constantSite(c, c->client->getThunk(type, firstSize, resultSize))), 0, 0, result, resultSize, argumentStack, ceiling(firstSize, BytesPerWord), 0); } else { @@ -3595,8 +3819,8 @@ appendMemory(Context* c, Value* base, int displacement, Value* index, class BranchEvent: public Event { public: - BranchEvent(Context* c, UnaryOperation type, Value* address): - Event(c), type(type), address(address) + BranchEvent(Context* c, UnaryOperation type, Value* address, bool exit): + Event(c), type(type), address(address), exit(exit) { address->addPredecessor(c, this); @@ -3606,7 +3830,7 @@ class BranchEvent: public Event { c->arch->plan(type, BytesPerWord, &typeMask, ®isterMask, &thunk); - assert(c, thunk == 0); + assert(c, not thunk); addRead(c, this, address, read (c, SiteMask(typeMask, registerMask, AnyFrameIndex))); @@ -3673,7 +3897,7 @@ class BranchEvent: public Event { jump = true; } - if (jump) { + if (jump and not unreachable(this)) { apply(c, type, BytesPerWord, address->source, 0); } @@ -3682,15 +3906,21 @@ class BranchEvent: public Event { virtual bool isBranch() { return true; } + virtual bool allExits() { + return type == Jump and (exit or unreachable(this)); + } + UnaryOperation type; Value* address; + bool exit; }; void -appendBranch(Context* c, UnaryOperation type, Value* address) +appendBranch(Context* c, UnaryOperation type, Value* address, + bool exit = false) { append(c, new (c->zone->allocate(sizeof(BranchEvent))) - BranchEvent(c, type, address)); + BranchEvent(c, type, address, exit)); } class BoundsCheckEvent: public Event { @@ -3802,11 +4032,11 @@ frameFootprint(Context* c, Stack* s) void visit(Context* c, Link* link) { -// fprintf(stderr, "visit link from %d to %d fork %p junction %p\n", -// link->predecessor->logicalInstruction->index, -// link->successor->logicalInstruction->index, -// link->forkState, -// link->junctionState); + // fprintf(stderr, "visit link from %d to %d fork %p junction %p\n", + // link->predecessor->logicalInstruction->index, + // link->successor->logicalInstruction->index, + // link->forkState, + // link->junctionState); ForkState* forkState = link->forkState; if (forkState) { @@ -3814,7 +4044,7 @@ visit(Context* c, Link* link) ForkElement* p = forkState->elements + i; Value* v = p->value; v->reads = p->read->nextTarget(); -// fprintf(stderr, "next read %p for %p from %p\n", v->reads, v, p->read); + // fprintf(stderr, "next read %p for %p from %p\n", v->reads, v, p->read); if (not live(v)) { clearSites(c, v); } @@ -3839,7 +4069,7 @@ class BuddyEvent: public Event { BuddyEvent(Context* c, Value* original, Value* buddy): Event(c), original(original), buddy(buddy) { - addRead(c, this, original, read(c, SiteMask(~0, ~0, AnyFrameIndex))); + addRead(c, this, original, read(c, SiteMask(~0, c->arch->allRegisters(), AnyFrameIndex))); } virtual const char* name() { @@ -3847,7 +4077,7 @@ class BuddyEvent: public Event { } virtual void compile(Context* c) { -// fprintf(stderr, "original %p buddy %p\n", original, buddy); + // fprintf(stderr, "original %p buddy %p\n", original, buddy); assert(c, hasSite(original)); addBuddy(original, buddy); @@ -3892,6 +4122,31 @@ appendSaveLocals(Context* c) SaveLocalsEvent(c)); } +class CleanLocalsEvent: public Event { + public: + CleanLocalsEvent(Context* c): + Event(c) + { } + + virtual const char* name() { + return "CleanLocalsEvent"; + } + + virtual void compile(Context* c) { + for (FrameIterator it(c, 0, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, 0); + } + } +}; + +void +appendCleanLocals(Context* c) +{ + append(c, new (c->zone->allocate(sizeof(CleanLocalsEvent))) + CleanLocalsEvent(c)); +} + class DummyEvent: public Event { public: DummyEvent(Context* c): @@ -3988,7 +4243,7 @@ acceptForResolve(Context* c, Site* s, Read* read, const SiteMask& mask) { if (acceptMatch(c, s, read, mask) and (not s->frozen(c))) { if (s->type(c) == RegisterOperand) { - return c->availableRegisterCount > ResolveRegisterReserveCount; + return c->generalRegisterCount > ResolveRegisterReserveCount; } else { assert(c, s->match(c, SiteMask(1 << MemoryOperand, 0, AnyFrameIndex))); @@ -4007,7 +4262,7 @@ pickSourceSite(Context* c, Read* read, Site* target = 0, bool (*accept)(Context*, Site*, Read*, const SiteMask&) = acceptMatch) { - SiteMask mask(typeMask, ~0, AnyFrameIndex); + SiteMask mask(typeMask, c->arch->allRegisters(), AnyFrameIndex); if (intersectRead) { read->intersect(&mask); @@ -4382,9 +4637,9 @@ void restore(Context* c, Event* e, Snapshot* snapshots) { for (Snapshot* s = snapshots; s; s = s->next) { -// char buffer[256]; sitesToString(c, s->sites, buffer, 256); -// fprintf(stderr, "restore %p buddy %p sites %s live %p\n", -// s->value, s->value->buddy, buffer, live(s->value)); + // char buffer[256]; sitesToString(c, s->sites, buffer, 256); + // fprintf(stderr, "restore %p buddy %p sites %s live %p\n", + // s->value, s->value->buddy, buffer, live(s->value)); s->value->buddy = s->buddy; } @@ -4736,7 +4991,7 @@ Value* maybeBuddy(Context* c, Value* v) { if (v->home >= 0) { - Value* n = value(c); + Value* n = value(c, v->type); appendBuddy(c, v, n); return n; } else { @@ -4801,11 +5056,12 @@ class MyCompiler: public Compiler { } virtual void endSubroutine(Subroutine* subroutine) { + appendCleanLocals(&c); static_cast(subroutine)->forkState = ::saveState(&c); } - virtual void restoreFromSubroutine(Subroutine* subroutine) { - ::restoreState(&c, static_cast(subroutine)->forkState); + virtual void linkSubroutine(Subroutine* subroutine) { + restoreState(static_cast(subroutine)->forkState); } virtual void init(unsigned logicalCodeLength, unsigned parameterFootprint, @@ -4816,7 +5072,7 @@ class MyCompiler: public Compiler { c.localFootprint = localFootprint; c.alignedFrameSize = alignedFrameSize; - unsigned frameResourceCount = usableFrameSize(&c) + parameterFootprint; + unsigned frameResourceCount = totalFrameSize(&c); c.frameResources = static_cast (c.zone->allocate(sizeof(FrameResource) * frameResourceCount)); @@ -4825,9 +5081,12 @@ class MyCompiler: public Compiler { new (c.frameResources + i) FrameResource; } + unsigned base = frameBase(&c); + c.frameResources[base + c.arch->returnAddressOffset()].reserved = true; + c.frameResources[base + c.arch->framePointerOffset()].reserved = true; + // leave room for logical instruction -1 - unsigned codeSize = sizeof(LogicalInstruction*) - * (logicalCodeLength + 1); + unsigned codeSize = sizeof(LogicalInstruction*) * (logicalCodeLength + 1); c.logicalCode = static_cast (c.zone->allocate(codeSize)); memset(c.logicalCode, 0, codeSize); @@ -4903,12 +5162,30 @@ class MyCompiler: public Compiler { (c.zone->allocate(sizeof(LogicalInstruction))) LogicalInstruction(logicalIp, c.stack, c.locals); - if (c.subroutine) { + bool startSubroutine = c.subroutine != 0; + if (startSubroutine) { c.logicalCode[logicalIp]->subroutine = c.subroutine; c.subroutine = 0; } c.logicalIp = logicalIp; + + if (startSubroutine) { + // assume all local variables are initialized on entry to a + // subroutine, since other calls to the subroutine may + // initialize them: + unsigned sizeInBytes = sizeof(Local) * c.localFootprint; + Local* newLocals = static_cast(c.zone->allocate(sizeInBytes)); + memcpy(newLocals, c.locals, sizeInBytes); + c.locals = newLocals; + + for (unsigned li = 0; li < c.localFootprint; ++li) { + Local* local = c.locals + li; + if (local->value == 0) { + initLocal(1, li, ValueFloat); + } + } + } } virtual Promise* machineIp(unsigned logicalIp) { @@ -4938,24 +5215,25 @@ class MyCompiler: public Compiler { return p; } - virtual Operand* constant(int64_t value) { - return promiseConstant(resolved(&c, value)); + virtual Operand* constant(int64_t value, unsigned code) { + return promiseConstant(resolved(&c, value), code); } - virtual Operand* promiseConstant(Promise* value) { - return ::value(&c, ::constantSite(&c, value)); + virtual Operand* promiseConstant(Promise* value, unsigned code) { + return ::value(&c, resultType(code), ::constantSite(&c, value)); } virtual Operand* address(Promise* address) { - return value(&c, ::addressSite(&c, address)); + return value(&c, ValueGeneral, ::addressSite(&c, address)); } virtual Operand* memory(Operand* base, + unsigned code, int displacement = 0, Operand* index = 0, unsigned scale = 1) { - Value* result = value(&c); + Value* result = value(&c, resultType(code)); appendMemory(&c, static_cast(base), displacement, static_cast(index), scale, result); @@ -4963,14 +5241,17 @@ class MyCompiler: public Compiler { return result; } - virtual Operand* stack() { - Site* s = registerSite(&c, c.arch->stack()); - return value(&c, s, s); - } - - virtual Operand* thread() { - Site* s = registerSite(&c, c.arch->thread()); - return value(&c, s, s); + virtual Operand* register_(int number) { + assert(&c, (1 << number) & c.arch->allRegisters()); + + Site* s = registerSite(&c, number); + ValueType type = ValueGeneral; + if((1 << number) & c.arch->generalRegisters()) { + type = ValueGeneral; + } else if((1 << number) & c.arch->floatRegisters()) { + type = ValueFloat; + } + return value(&c, type, s, s); } Promise* machineIp() { @@ -4980,14 +5261,14 @@ class MyCompiler: public Compiler { virtual void push(unsigned footprint UNUSED) { assert(&c, footprint == 1); - Value* v = value(&c); + Value* v = value(&c, ValueFloat); Stack* s = ::stack(&c, v, c.stack); v->home = frameIndex(&c, s->index + c.localFootprint); c.stack = s; } virtual void push(unsigned footprint, Operand* value) { - ::push(&c, footprint, static_cast(value)); + ::push(&c, footprint, static_cast(value), true); } virtual void save(unsigned footprint, Operand* value) { @@ -5005,7 +5286,7 @@ class MyCompiler: public Compiler { } virtual void pushed() { - Value* v = value(&c); + Value* v = value(&c, ValueFloat); appendFrameSite (&c, v, frameIndex (&c, (c.stack ? c.stack->index : 0) + c.localFootprint)); @@ -5016,35 +5297,19 @@ class MyCompiler: public Compiler { } virtual void popped(unsigned footprint) { - assert(&c, c.stack->value->home >= 0); + for (; footprint; -- footprint) { + assert(&c, c.stack->value == 0 or c.stack->value->home >= 0); - if (footprint > 1) { - assert(&c, footprint == 2); - assert(&c, c.stack->value->high == c.stack->next->value - and ((BytesPerWord == 8) xor (c.stack->value->high != 0))); - - popped(1); + if (DebugFrame) { + fprintf(stderr, "popped %p\n", c.stack->value); + } + + c.stack = c.stack->next; } - - if (DebugFrame) { - fprintf(stderr, "popped %p\n", c.stack->value); - } - - c.stack = c.stack->next; } - virtual StackElement* top() { - return c.stack; - } - - virtual unsigned footprint(StackElement* e) { - return (static_cast(e)->next - and (static_cast(e)->next->value - == static_cast(e)->value->high)) ? 2 : 1; - } - - virtual unsigned index(StackElement* e) { - return static_cast(e)->index; + virtual unsigned topOfStack() { + return c.stack->index; } virtual Operand* peek(unsigned footprint, unsigned index) { @@ -5062,18 +5327,18 @@ class MyCompiler: public Compiler { Stack* low; Stack* high; if (bigEndian) { - high = s; - low = s->next; - } else { low = s; high = s->next; + } else { + high = s; + low = s->next; } assert(&c, low->value->high == high->value and ((BytesPerWord == 8) xor (low->value->high != 0))); #endif // not NDEBUG - if (bigEndian) { + if (not bigEndian) { s = s->next; } } @@ -5085,6 +5350,7 @@ class MyCompiler: public Compiler { unsigned flags, TraceHandler* traceHandler, unsigned resultSize, + unsigned resultCode, unsigned argumentCount, ...) { @@ -5121,7 +5387,7 @@ class MyCompiler: public Compiler { argumentStack = ::stack(&c, arguments[i], argumentStack); } - Value* result = value(&c); + Value* result = value(&c, resultType(resultCode)); appendCall(&c, static_cast(address), flags, traceHandler, result, resultSize, argumentStack, index, 0); @@ -5132,9 +5398,10 @@ class MyCompiler: public Compiler { unsigned flags, TraceHandler* traceHandler, unsigned resultSize, + unsigned resultCode, unsigned argumentFootprint) { - Value* result = value(&c); + Value* result = value(&c, resultType(resultCode)); appendCall(&c, static_cast(address), flags, traceHandler, result, resultSize, c.stack, 0, argumentFootprint); return result; @@ -5144,10 +5411,10 @@ class MyCompiler: public Compiler { appendReturn(&c, size, static_cast(value)); } - virtual void initLocal(unsigned footprint, unsigned index) { + virtual void initLocal(unsigned footprint, unsigned index, unsigned code) { assert(&c, index + footprint <= c.localFootprint); - Value* v = value(&c); + Value* v = value(&c, resultType(code)); if (footprint > 1) { assert(&c, footprint == 2); @@ -5163,7 +5430,7 @@ class MyCompiler: public Compiler { } if (BytesPerWord == 4) { - initLocal(1, highIndex); + initLocal(1, highIndex, code); v->high = c.locals[highIndex].value; } @@ -5194,7 +5461,7 @@ class MyCompiler: public Compiler { for (int i = 0; i < static_cast(c.localFootprint); ++i) { Local* local = e->localsBefore + i; if (local->value) { - initLocal(1, i); + initLocal(1, i, local->value->type == ValueGeneral ? IntField : FloatField); } } @@ -5244,7 +5511,7 @@ class MyCompiler: public Compiler { { assert(&c, dstSize >= BytesPerWord); - Value* dst = value(&c); + Value* dst = value(&c, static_cast(src)->type); appendMove(&c, Move, srcSize, srcSelectSize, static_cast(src), dstSize, dst); return dst; @@ -5255,31 +5522,30 @@ class MyCompiler: public Compiler { { assert(&c, dstSize >= BytesPerWord); - Value* dst = value(&c); + Value* dst = value(&c, static_cast(src)->type); appendMove(&c, MoveZ, srcSize, srcSelectSize, static_cast(src), dstSize, dst); return dst; } virtual Operand* lcmp(Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, LongCompare, 8, static_cast(a), 8, static_cast(b), 8, result); return result; } virtual void cmp(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); appendCompare(&c, Compare, size, static_cast(a), static_cast(b)); } virtual void fcmp(unsigned size, Operand* a, Operand* b) { - static_cast(a)->type = ValueFloat; - static_cast(b)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); appendCompare(&c, FloatCompare, size, static_cast(a), static_cast(b)); - //static_cast(a)->type = ValueGeneral; - //static_cast(b)->type = ValueGeneral; } @@ -5306,52 +5572,86 @@ class MyCompiler: public Compiler { virtual void jne(Operand* address) { appendBranch(&c, JumpIfNotEqual, static_cast(address)); } + + virtual void fjl(Operand* address) { + appendBranch(&c, JumpIfFloatLess, static_cast(address)); + } + + virtual void fjg(Operand* address) { + appendBranch(&c, JumpIfFloatGreater, static_cast(address)); + } + + virtual void fjle(Operand* address) { + appendBranch(&c, JumpIfFloatLessOrEqual, static_cast(address)); + } + + virtual void fjge(Operand* address) { + appendBranch(&c, JumpIfFloatGreaterOrEqual, static_cast(address)); + } + + virtual void fje(Operand* address) { + appendBranch(&c, JumpIfFloatEqual, static_cast(address)); + } + + virtual void fjne(Operand* address) { + appendBranch(&c, JumpIfFloatNotEqual, static_cast(address)); + } - virtual void juo(Operand* address) { - appendBranch(&c, JumpIfUnordered, static_cast(address)); + virtual void fjuo(Operand* address) { + appendBranch(&c, JumpIfFloatUnordered, static_cast(address)); } virtual void jmp(Operand* address) { appendBranch(&c, Jump, static_cast(address)); } + virtual void exit(Operand* address) { + appendBranch(&c, Jump, static_cast(address), true); + } + virtual Operand* add(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Add, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* sub(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Subtract, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* mul(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Multiply, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* div(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Divide, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* rem(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral and static_cast(b)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Remainder, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* fadd(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); + Value* result = value(&c, ValueFloat); static_cast(a)->type = static_cast(b)->type = ValueFloat; appendCombine(&c, FloatAdd, size, static_cast(a), size, static_cast(b), size, result); @@ -5359,7 +5659,8 @@ class MyCompiler: public Compiler { } virtual Operand* fsub(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); + Value* result = value(&c, ValueFloat); static_cast(a)->type = static_cast(b)->type = ValueFloat; appendCombine(&c, FloatSubtract, size, static_cast(a), size, static_cast(b), size, result); @@ -5367,7 +5668,8 @@ class MyCompiler: public Compiler { } virtual Operand* fmul(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); + Value* result = value(&c, ValueFloat); static_cast(a)->type = static_cast(b)->type = ValueFloat; appendCombine(&c, FloatMultiply, size, static_cast(a), size, static_cast(b), size, result); @@ -5375,110 +5677,114 @@ class MyCompiler: public Compiler { } virtual Operand* fdiv(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); - static_cast(a)->type = static_cast(b)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); + Value* result = value(&c, ValueFloat); appendCombine(&c, FloatDivide, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* frem(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); - static_cast(a)->type = static_cast(b)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat and static_cast(b)->type == ValueFloat); + Value* result = value(&c, ValueFloat); appendCombine(&c, FloatRemainder, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* shl(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, ShiftLeft, BytesPerWord, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* shr(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, ShiftRight, BytesPerWord, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* ushr(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, UnsignedShiftRight, BytesPerWord, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* and_(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, And, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* or_(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Or, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* xor_(unsigned size, Operand* a, Operand* b) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendCombine(&c, Xor, size, static_cast(a), size, static_cast(b), size, result); return result; } virtual Operand* neg(unsigned size, Operand* a) { - Value* result = value(&c); + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueGeneral); appendTranslate(&c, Negate, size, static_cast(a), size, result); return result; } virtual Operand* fneg(unsigned size, Operand* a) { - Value* result = value(&c); - static_cast(a)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat); + Value* result = value(&c, ValueFloat); appendTranslate(&c, FloatNegate, size, static_cast(a), size, result); return result; } - virtual Operand* operation(BinaryOperation op, unsigned aSize, unsigned resSize, Operand* a) { - Value* result = value(&c); - static_cast(a)->type = ValueFloat; + virtual Operand* operation(BinaryOperation op, unsigned aSize, unsigned resSize, unsigned resCode, Operand* a) { + Value* result = value(&c, resultType(resCode)); appendTranslate(&c, op, aSize, static_cast(a), resSize, result); return result; } - virtual Operand* operation(TernaryOperation op, unsigned aSize, unsigned bSize, unsigned resSize, Operand* a, Operand* b) { - Value* result = value(&c); - static_cast(a)->type = static_cast(b)->type = ValueFloat; + virtual Operand* operation(TernaryOperation op, unsigned aSize, unsigned bSize, unsigned resSize, unsigned resCode, Operand* a, Operand* b) { + Value* result = value(&c, resultType(resCode)); appendCombine(&c, op, aSize, static_cast(a), bSize, static_cast(b), resSize, result); return result; } virtual Operand* f2f(unsigned aSize, unsigned resSize, Operand* a) { - Value* result = value(&c); - static_cast(a)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat); + Value* result = value(&c, ValueFloat); appendTranslate(&c, Float2Float, aSize, static_cast(a), resSize, result); return result; } virtual Operand* f2i(unsigned aSize, unsigned resSize, Operand* a) { - Value* result = value(&c); - static_cast(a)->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueFloat); + Value* result = value(&c, ValueGeneral); appendTranslate(&c, Float2Int, aSize, static_cast(a), resSize, result); return result; } virtual Operand* i2f(unsigned aSize, unsigned resSize, Operand* a) { - Value* result = value(&c); - //result->type = ValueFloat; + assert(&c, static_cast(a)->type == ValueGeneral); + Value* result = value(&c, ValueFloat); appendTranslate(&c, Int2Float, aSize, static_cast(a), resSize, result); - //result->type = ValueGeneral; return result; } diff --git a/src/compiler.h b/src/compiler.h index 8d78720e25..234768f350 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -17,6 +17,11 @@ namespace vm { +class TraceHandler { + public: + virtual void handleTrace(Promise* address, unsigned argumentIndex) = 0; +}; + class Compiler { public: class Client { @@ -28,9 +33,9 @@ class Compiler { static const unsigned Aligned = 1 << 0; static const unsigned NoReturn = 1 << 1; + static const unsigned TailJump = 1 << 2; class Operand { }; - class StackElement { }; class State { }; class Subroutine { }; @@ -39,7 +44,7 @@ class Compiler { virtual Subroutine* startSubroutine() = 0; virtual void endSubroutine(Subroutine* subroutine) = 0; - virtual void restoreFromSubroutine(Subroutine* subroutine) = 0; + virtual void linkSubroutine(Subroutine* subroutine) = 0; virtual void init(unsigned logicalCodeSize, unsigned parameterFootprint, unsigned localFootprint, unsigned alignedFrameSize) = 0; @@ -52,16 +57,16 @@ class Compiler { virtual Promise* poolAppend(intptr_t value) = 0; virtual Promise* poolAppendPromise(Promise* value) = 0; - virtual Operand* constant(int64_t value) = 0; - virtual Operand* promiseConstant(Promise* value) = 0; + virtual Operand* constant(int64_t value, unsigned code) = 0; + virtual Operand* promiseConstant(Promise* value, unsigned code) = 0; virtual Operand* address(Promise* address) = 0; virtual Operand* memory(Operand* base, + unsigned code, int displacement = 0, Operand* index = 0, unsigned scale = 1) = 0; - virtual Operand* stack() = 0; - virtual Operand* thread() = 0; + virtual Operand* register_(int number) = 0; virtual void push(unsigned footprint) = 0; virtual void push(unsigned footprint, Operand* value) = 0; @@ -69,15 +74,14 @@ class Compiler { virtual Operand* pop(unsigned footprint) = 0; virtual void pushed() = 0; virtual void popped(unsigned footprint) = 0; - virtual StackElement* top() = 0; - virtual unsigned footprint(StackElement*) = 0; - virtual unsigned index(StackElement*) = 0; + virtual unsigned topOfStack() = 0; virtual Operand* peek(unsigned footprint, unsigned index) = 0; virtual Operand* call(Operand* address, unsigned flags, TraceHandler* traceHandler, unsigned resultSize, + unsigned resultCode, unsigned argumentCount, ...) = 0; @@ -85,11 +89,12 @@ class Compiler { unsigned flags, TraceHandler* traceHandler, unsigned resultSize, + unsigned resultCode, unsigned argumentFootprint) = 0; virtual void return_(unsigned size, Operand* value) = 0; - virtual void initLocal(unsigned size, unsigned index) = 0; + virtual void initLocal(unsigned size, unsigned index, unsigned code) = 0; virtual void initLocalsFromLogicalIp(unsigned logicalIp) = 0; virtual void storeLocal(unsigned footprint, Operand* src, unsigned index) = 0; @@ -114,8 +119,15 @@ class Compiler { virtual void jge(Operand* address) = 0; virtual void je(Operand* address) = 0; virtual void jne(Operand* address) = 0; - virtual void juo(Operand* address) = 0; + virtual void fjl(Operand* address) = 0; + virtual void fjg(Operand* address) = 0; + virtual void fjle(Operand* address) = 0; + virtual void fjge(Operand* address) = 0; + virtual void fje(Operand* address) = 0; + virtual void fjne(Operand* address) = 0; + virtual void fjuo(Operand* address) = 0; virtual void jmp(Operand* address) = 0; + virtual void exit(Operand* address) = 0; virtual Operand* add(unsigned size, Operand* a, Operand* b) = 0; virtual Operand* sub(unsigned size, Operand* a, Operand* b) = 0; virtual Operand* mul(unsigned size, Operand* a, Operand* b) = 0; @@ -134,8 +146,8 @@ class Compiler { virtual Operand* xor_(unsigned size, Operand* a, Operand* b) = 0; virtual Operand* neg(unsigned size, Operand* a) = 0; virtual Operand* fneg(unsigned size, Operand* a) = 0; - virtual Operand* operation(BinaryOperation op, unsigned aSize, unsigned resSize, Operand* a) = 0; - virtual Operand* operation(TernaryOperation op, unsigned aSize, unsigned bSize, unsigned resSize, Operand* a, Operand* b) = 0; + virtual Operand* operation(BinaryOperation op, unsigned aSize, unsigned resSize, unsigned resCode, Operand* a) = 0; + virtual Operand* operation(TernaryOperation op, unsigned aSize, unsigned bSize, unsigned resSize, unsigned resCode, Operand* a, Operand* b) = 0; virtual Operand* f2f(unsigned aSize, unsigned resSize, Operand* a) = 0; virtual Operand* f2i(unsigned aSize, unsigned resSize, Operand* a) = 0; virtual Operand* i2f(unsigned aSize, unsigned resSize, Operand* a) = 0; diff --git a/src/continuations-x86.S b/src/continuations-x86.S new file mode 100644 index 0000000000..bc19003758 --- /dev/null +++ b/src/continuations-x86.S @@ -0,0 +1,161 @@ +#ifdef __x86_64__ + +#define THREAD_CONTINUATION 168 +#define THREAD_EXCEPTION 64 +#define THREAD_EXCEPTION_STACK_ADJUSTMENT 176 +#define THREAD_EXCEPTION_OFFSET 184 +#define THREAD_EXCEPTION_HANDLER 192 + +#define CONTINUATION_NEXT 8 +#define CONTINUATION_ADDRESS 32 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 40 +#define CONTINUATION_FRAME_POINTER_OFFSET 48 +#define CONTINUATION_LENGTH 56 +#define CONTINUATION_BODY 64 + + // call the next continuation, if any + movq THREAD_CONTINUATION(%rbx),%rcx + cmpq $0,%rcx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD) + // + CALLEE_SAVED_REGISTER_FOOTPRINT + movq CONTINUATION_LENGTH(%rcx),%rsi + shlq $3,%rsi + subq %rsi,%rsp + subq $CALLEE_SAVED_REGISTER_FOOTPRINT,%rsp + + // copy the continuation body into the frame + leaq CONTINUATION_BODY(%rcx),%rdi + + movq $0,%r9 + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movq (%rdi,%r9,1),%r8 + movq %r8,(%rsp,%r9,1) + addq $8,%r9 + +LOCAL(vmInvoke_continuationTest): + cmpq %rsi,%r9 + jb LOCAL(vmInvoke_continuationLoop) + + // set the return address to vmInvoke_returnAddress + movq CONTINUATION_RETURN_ADDRESS_OFFSET(%rcx),%rdi + movq vmInvoke_returnAddress@GOTPCREL(%rip),%r10 + movq %r10,(%rsp,%rdi,1) + + // save the current base pointer in the frame and update it + movq CONTINUATION_FRAME_POINTER_OFFSET(%rcx),%rdi + movq %rbp,(%rsp,%rdi,1) + addq %rsp,%rdi + movq %rdi,%rbp + + // consume the continuation + movq CONTINUATION_NEXT(%rcx),%rdi + movq %rdi,THREAD_CONTINUATION(%rbx) + + // call the continuation unless we're handling an exception + movq THREAD_EXCEPTION(%rbx),%rsi + cmpq $0,%rsi + jne LOCAL(vmInvoke_handleException) + jmp *CONTINUATION_ADDRESS(%rcx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movq $0,THREAD_EXCEPTION(%rbx) + movq THREAD_EXCEPTION_STACK_ADJUSTMENT(%rbx),%rdi + subq %rdi,%rsp + movq THREAD_EXCEPTION_OFFSET(%rbx),%rdi + movq %rsi,(%rsp,%rdi,1) + + jmp *THREAD_EXCEPTION_HANDLER(%rbx) + +LOCAL(vmInvoke_exit): + +#elif defined __i386__ + +#define THREAD_CONTINUATION 96 +#define THREAD_EXCEPTION 36 +#define THREAD_EXCEPTION_STACK_ADJUSTMENT 100 +#define THREAD_EXCEPTION_OFFSET 104 +#define THREAD_EXCEPTION_HANDLER 108 + +#define CONTINUATION_NEXT 4 +#define CONTINUATION_ADDRESS 16 +#define CONTINUATION_RETURN_ADDRESS_OFFSET 20 +#define CONTINUATION_FRAME_POINTER_OFFSET 24 +#define CONTINUATION_LENGTH 28 +#define CONTINUATION_BODY 32 + + // call the next continuation, if any + movl THREAD_CONTINUATION(%ebx),%ecx + cmpl $0,%ecx + je LOCAL(vmInvoke_exit) + + // allocate a frame of size (continuation.length * BYTES_PER_WORD) + movl CONTINUATION_LENGTH(%ecx),%esi + shll $2,%esi + subl %esi,%esp + + // copy the continuation body into the frame + leal CONTINUATION_BODY(%ecx),%edi + + push %eax + push %edx + + movl $0,%edx + jmp LOCAL(vmInvoke_continuationTest) + +LOCAL(vmInvoke_continuationLoop): + movl (%edi,%edx,1),%eax + movl %eax,8(%esp,%edx,1) + addl $4,%edx + +LOCAL(vmInvoke_continuationTest): + cmpl %esi,%edx + jb LOCAL(vmInvoke_continuationLoop) + + pop %edx + pop %eax + + // set the return address to vmInvoke_returnAddress + movl CONTINUATION_RETURN_ADDRESS_OFFSET(%ecx),%edi + call LOCAL(getPC) + addl $_GLOBAL_OFFSET_TABLE_,%esi + movl vmInvoke_returnAddress@GOT(%esi),%esi + movl %esi,(%esp,%edi,1) + + // save the current base pointer in the frame and update it + movl CONTINUATION_FRAME_POINTER_OFFSET(%ecx),%edi + movl %ebp,(%esp,%edi,1) + addl %esp,%edi + movl %edi,%ebp + + // consume the continuation + movl CONTINUATION_NEXT(%ecx),%edi + movl %edi,THREAD_CONTINUATION(%ebx) + + // call the continuation unless we're handling an exception + movl THREAD_EXCEPTION(%ebx),%esi + cmpl $0,%esi + jne LOCAL(vmInvoke_handleException) + + jmp *CONTINUATION_ADDRESS(%ecx) + +LOCAL(vmInvoke_handleException): + // we're handling an exception - call the exception handler instead + movl $0,THREAD_EXCEPTION(%ebx) + movl THREAD_EXCEPTION_STACK_ADJUSTMENT(%ebx),%edi + subl %edi,%esp + movl THREAD_EXCEPTION_OFFSET(%ebx),%edi + movl %esi,(%esp,%edi,1) + + jmp *THREAD_EXCEPTION_HANDLER(%ebx) + +LOCAL(vmInvoke_exit): + +#else +# error unsupported architecture +#endif + diff --git a/src/gnu.cpp b/src/gnu.cpp new file mode 100644 index 0000000000..1aa77106b5 --- /dev/null +++ b/src/gnu.cpp @@ -0,0 +1,413 @@ +/* Copyright (c) 2009, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +#include "machine.h" +#include "constants.h" +#include "processor.h" + +using namespace vm; + +namespace { + +void +setProperty(Thread* t, object method, object properties, + const char* name, const void* value, const char* format = "%s") +{ + PROTECT(t, method); + PROTECT(t, properties); + + object n = makeString(t, "%s", name); + PROTECT(t, n); + + object v = makeString(t, format, value); + + t->m->processor->invoke(t, method, properties, n, v); +} + +} // namespace + +namespace vm { + +jobject JNICALL +NewDirectByteBuffer(Thread* t, void* address, jlong capacity) +{ + const char* pointerClassName; + const char* initSpec; + if (BytesPerWord == 8) { + pointerClassName = "gnu/classpath/Pointer64"; + initSpec = "(J)V"; + } else { + pointerClassName = "gnu/classpath/Pointer32"; + initSpec = "(I)V"; + } + + object pointerClass = resolveClass(t, pointerClassName); + if (UNLIKELY(pointerClass == 0)) return 0; + PROTECT(t, pointerClass); + + object pointerConstructor = resolveMethod + (t, pointerClass, "", initSpec); + if (UNLIKELY(pointerConstructor == 0)) return 0; + + object pointer = make(t, pointerClass); + PROTECT(t, pointer); + + t->m->processor->invoke(t, pointerConstructor, pointer, address); + if (UNLIKELY(t->exception)) return 0; + + object bufferClass = resolveClass + (t, "java/nio/DirectByteBufferImpl$ReadWrite"); + if (UNLIKELY(bufferClass == 0)) return 0; + PROTECT(t, bufferClass); + + object bufferConstructor = resolveMethod + (t, bufferClass, "", "(Lgnu/classpath/Pointer;int)V"); + if (UNLIKELY(bufferConstructor == 0)) return 0; + + object buffer = make(t, bufferClass); + PROTECT(t, buffer); + + t->m->processor->invoke + (t, bufferConstructor, buffer, &pointer, static_cast(capacity)); + if (UNLIKELY(t->exception)) return 0; + + return makeLocalReference(t, buffer); +} + +void* JNICALL +GetDirectBufferAddress(Thread* t, jobject buffer) +{ + object addressField = resolveField + (t, objectClass(t, *buffer), "address", "Lgnu/classpath/Pointer;"); + if (UNLIKELY(addressField == 0)) return 0; + + object address = cast(*buffer, fieldOffset(t, addressField)); + if (address == 0) return 0; + + const char* dataSpec; + if (BytesPerWord == 8) { + dataSpec = "J"; + } else { + dataSpec = "I"; + } + + object dataField = resolveField + (t, objectClass(t, address), "data", dataSpec); + if (UNLIKELY(dataField == 0)) return 0; + + return cast(address, fieldOffset(t, dataField)); +} + +jlong JNICALL +GetDirectBufferCapacity(Thread* t, jobject buffer) +{ + object capField = resolveField(t, objectClass(t, *buffer), "cap", "I"); + if (UNLIKELY(capField == 0)) return 0; + + return cast(*buffer, fieldOffset(t, capField)); +} + +} // namespace vm + +extern "C" JNIEXPORT void JNICALL +Avian_gnu_classpath_VMSystemProperties_preInit +(Thread* t, object, uintptr_t* arguments) +{ + object properties = reinterpret_cast(arguments[0]); + PROTECT(t, properties); + + object method = resolveMethod + (t, "java/util/Properties", "setProperty", + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"); + + if (UNLIKELY(t->exception)) { + return; + } + + PROTECT(t, method); + + setProperty(t, method, properties, "java.vm.name", "Avian"); + + setProperty(t, method, properties, "java.protocol.handler.pkgs", "avian"); + + setProperty(t, method, properties, "file.encoding", "ASCII"); + + // specify a bogus library path so we can do our own search in + // VMRuntime.nativeLoad: +#define LIBRARY_PATH_SENTINAL "*" + setProperty(t, method, properties, "java.library.path", + LIBRARY_PATH_SENTINAL); + +#ifdef WIN32 +# define FILE_SEPARATOR "\\" + + setProperty(t, method, properties, "line.separator", "\r\n"); + setProperty(t, method, properties, "file.separator", FILE_SEPARATOR); + setProperty(t, method, properties, "path.separator", ";"); + setProperty(t, method, properties, "os.name", "Windows"); + + TCHAR buffer[MAX_PATH]; + GetTempPath(MAX_PATH, buffer); + setProperty(t, method, properties, "java.io.tmpdir", buffer); + + setProperty(t, method, properties, "user.home", + _wgetenv(L"USERPROFILE"), "%ls"); + + GetCurrentDirectory(MAX_PATH, buffer); + setProperty(t, method, properties, "user.dir", buffer); +#else +# define FILE_SEPARATOR "/" + + setProperty(t, method, properties, "line.separator", "\n"); + setProperty(t, method, properties, "file.separator", FILE_SEPARATOR); + setProperty(t, method, properties, "path.separator", ":"); +# ifdef __APPLE__ + setProperty(t, method, properties, "os.name", "Mac OS X"); +# else + setProperty(t, method, properties, "os.name", "Linux"); +# endif + setProperty(t, method, properties, "java.io.tmpdir", "/tmp"); + setProperty(t, method, properties, "user.home", getenv("HOME")); + setProperty(t, method, properties, "user.dir", getenv("PWD")); +#endif + +#ifdef __i386__ + setProperty(t, method, properties, "os.arch", "x86"); +#elif defined __x86_64__ + setProperty(t, method, properties, "os.arch", "x86_64"); +#elif defined(__ppc__) || defined(__powerpc__) \ + || defined(__ppc64__) || defined(__powerpc64__) + setProperty(t, method, properties, "os.arch", "ppc"); +#elif defined __ia64__ + setProperty(t, method, properties, "os.arch", "ia64"); +#elif defined __arm__ + setProperty(t, method, properties, "os.arch", "arm"); +#elif defined __alpha__ + setProperty(t, method, properties, "os.arch", "alpha"); +#elif defined __sparc64__ + setProperty(t, method, properties, "os.arch", "sparc64"); +#else + setProperty(t, method, properties, "os.arch", "unknown"); +#endif +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_gnu_classpath_VMStackWalker_getClassContext +(Thread* t, object, uintptr_t*) +{ + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t): + t(t), skipCount(1), trace(0), index(0), protector(t, &trace) + { } + + virtual bool visit(Processor::StackWalker* walker) { + if (skipCount == 0) { + if (trace == 0) { + trace = makeObjectArray + (t, arrayBody(t, t->m->types, Machine::ClassType), + walker->count()); + } + + assert(t, index < objectArrayLength(t, trace)); + + set(t, trace, ArrayBody + (index * BytesPerWord), + methodClass(t, walker->method())); + + ++ index; + return true; + } else { + -- skipCount; + return true; + } + } + + Thread* t; + unsigned skipCount; + object trace; + unsigned index; + Thread::SingleProtector protector; + } v(t); + + t->m->processor->walkStack(t, &v); + + if (v.trace == 0) { + v.trace = makeObjectArray + (t, arrayBody(t, t->m->types, Machine::ClassType), 0); + } + + return reinterpret_cast(v.trace); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_gnu_classpath_VMStackWalker_getClassLoader +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (classLoader(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMRuntime_mapLibraryName +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + const unsigned soPrefixLength = sizeof(SO_PREFIX) - 1; + const unsigned nameLength = stringLength(t, name); + const unsigned soSuffixLength = sizeof(SO_SUFFIX) - 1; + const unsigned total = soPrefixLength + nameLength + soSuffixLength; + + object s = makeByteArray(t, total + 1); + char* p = reinterpret_cast(&byteArrayBody(t, s, 0)); + + memcpy(p, SO_PREFIX, soPrefixLength); + stringChars(t, name, p + soPrefixLength); + memcpy(p + soPrefixLength + nameLength, SO_SUFFIX, soSuffixLength); + p[total] = 0; + + return reinterpret_cast(makeString(t, s, 0, total, 0)); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_System_arraycopy +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMSystem_arraycopy +(Thread* t, object, uintptr_t* arguments) +{ + Avian_java_lang_System_arraycopy(t, 0, arguments); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Runtime_load +(Thread* t, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMRuntime_nativeLoad +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + + // given that we set java.library.path to LIBRARY_PATH_SENTINAL, we + // can determine which names are filenames and which are library + // names by looking for the prefix LIBRARY_PATH_SENTINAL + // FILE_SEPARATOR + + unsigned length = stringLength(t, name); + char n[length + 1]; + stringChars(t, name, n); + + const unsigned pathPrefixLength + = sizeof(LIBRARY_PATH_SENTINAL) - 1 + + sizeof(FILE_SEPARATOR) - 1; + + bool mapName = (strncmp(n, LIBRARY_PATH_SENTINAL FILE_SEPARATOR, + pathPrefixLength) == 0); + if (mapName) { + // strip the path prefix, SO prefix, and SO suffix before passing + // the name to Runtime.load + + const unsigned soPrefixLength = sizeof(SO_PREFIX) - 1; + const unsigned soSuffixLength = sizeof(SO_SUFFIX) - 1; + const unsigned newOffset + = stringOffset(t, name) + pathPrefixLength + soPrefixLength; + const unsigned newLength + = length - pathPrefixLength - soPrefixLength - soSuffixLength; + + name = makeString(t, stringData(t, name), newOffset, newLength, 0); + } + + uintptr_t args[] = { reinterpret_cast(name), mapName }; + + Avian_java_lang_Runtime_load(t, 0, args); + + if (t->exception) { + t->exception = 0; + return 0; + } else { + return 1; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_primitiveClass +(Thread* t, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_getPrimitiveClass +(Thread* t, object, uintptr_t* arguments) +{ + return Avian_java_lang_Class_primitiveClass(t, 0, arguments); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_identityHashCode +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMSystem_identityHashCode +(Thread* t, object, uintptr_t* arguments) +{ + return Avian_java_lang_System_identityHashCode(t, 0, arguments); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Runtime_gc +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_gc +(Thread* t, object, uintptr_t*) +{ + Avian_java_lang_Runtime_gc(t, 0, 0); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_runFinalizationForExit +(Thread*, object, uintptr_t*) +{ + // ignore +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMRuntime_exit +(Thread*, object, uintptr_t* arguments) +{ + exit(arguments[0]); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_loadClass +(Thread* t, object, uintptr_t* arguments) +{ + uintptr_t args[] = { 0, arguments[0] }; + + return Avian_avian_SystemClassLoader_findClass(t, 0, args); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findLoadedClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_findLoadedClass +(Thread* t, object, uintptr_t* arguments) +{ + uintptr_t args[] = { 0, arguments[1] }; + + return Avian_avian_SystemClassLoader_findLoadedClass(t, 0, args); +} diff --git a/src/heap.cpp b/src/heap.cpp index f04d5de2c3..5ec5ae724d 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -34,6 +34,12 @@ const bool Verbose2 = false; const bool Debug = false; const bool DebugFixies = false; +#ifdef NDEBUG +const bool DebugAllocation = false; +#else +const bool DebugAllocation = true; +#endif + #define ACQUIRE(x) MutexLock MAKE_NAME(monitorLock_) (x) class MutexLock { @@ -422,7 +428,9 @@ class Segment { } void dispose() { - free(context, data, (footprint(capacity())) * BytesPerWord); + if (data) { + free(context, data, (footprint(capacity())) * BytesPerWord); + } data = 0; map = 0; } @@ -1673,11 +1681,22 @@ void* tryAllocate(Context* c, unsigned size) { ACQUIRE(c->lock); + if (DebugAllocation) { + size = pad(size) + 2 * BytesPerWord; + } + if (size + c->count < c->limit) { void* p = c->system->tryAllocate(size); if (p) { c->count += size; - return p; + + if (DebugAllocation) { + static_cast(p)[0] = 0x22377322; + static_cast(p)[(size / BytesPerWord) - 1] = 0x22377322; + return static_cast(p) + 1; + } else { + return p; + } } } return 0; @@ -1686,7 +1705,21 @@ void* tryAllocate(Context* c, unsigned size) void free(Context* c, const void* p, unsigned size) { ACQUIRE(c->lock); + if (DebugAllocation) { + size = pad(size) + 2 * BytesPerWord; + + memset(const_cast(p), 0xFE, size - (2 * BytesPerWord)); + + p = static_cast(p) - 1; + + expect(c->system, static_cast(p)[0] == 0x22377322); + + expect(c->system, static_cast(p) + [(size / BytesPerWord) - 1] == 0x22377322); + } + expect(c->system, c->count >= size); + c->system->free(p); c->count -= size; } diff --git a/src/heapwalk.cpp b/src/heapwalk.cpp index f6e2b746fe..12128a6dd1 100644 --- a/src/heapwalk.cpp +++ b/src/heapwalk.cpp @@ -88,7 +88,7 @@ class Context { while (stack) { Stack* dead = stack; stack = dead->next; - thread->m->heap->free(stack, sizeof(Stack)); + thread->m->heap->free(dead, sizeof(Stack)); } } diff --git a/src/interpret.cpp b/src/interpret.cpp index f2e739fecf..b1d533eb1a 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -26,6 +26,8 @@ const unsigned FrameMethodOffset = 2; const unsigned FrameIpOffset = 3; const unsigned FrameFootprint = 4; +class ClassInitList; + class Thread: public vm::Thread { public: static const unsigned StackSizeInBytes = 64 * 1024; @@ -36,16 +38,39 @@ class Thread: public vm::Thread { ip(0), sp(0), frame(-1), - code(0) + code(0), + classInitList(0) { } unsigned ip; unsigned sp; int frame; object code; + ClassInitList* classInitList; uintptr_t stack[StackSizeInWords]; }; +class ClassInitList { + public: + ClassInitList(Thread* t, object class_, ClassInitList* next): + t(t), class_(class_), next(next) + { } + + static void push(Thread* t, object class_) { + t->classInitList = new (t->m->heap->allocate(sizeof(ClassInitList))) + ClassInitList(t, class_, t->classInitList); + } + + void pop() { + t->classInitList = next; + t->m->heap->free(this, sizeof(ClassInitList)); + } + + Thread* t; + object class_; + ClassInitList* next; +}; + inline void pushObject(Thread* t, object o) { @@ -142,7 +167,7 @@ popLong(Thread* t) return (b << 32) | a; } -inline float +inline double popDouble(Thread* t) { return bitsToDouble(popLong(t)); @@ -348,12 +373,14 @@ popFrame(Thread* t) } } - if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag)) { - if (t->exception) { - t->exception = makeExceptionInInitializerError(t, t->exception); - } - classVmFlags(t, methodClass(t, method)) &= ~(NeedInitFlag | InitFlag); - release(t, t->m->classLock); + if (UNLIKELY(methodVmFlags(t, method) & ClassInitFlag) + and t->classInitList) + { + assert(t, t->classInitList->class_ == methodClass(t, method)); + + t->classInitList->pop(); + + postInitClass(t, methodClass(t, method)); } t->sp = frameBase(t, t->frame); @@ -410,8 +437,7 @@ makeNativeMethodData(Thread* t, object method, void* function) object data = makeNativeMethodData(t, function, 0, // argument table size - count, - false); + count); unsigned argumentTableSize = BytesPerWord * 2; unsigned index = 0; @@ -460,7 +486,7 @@ makeNativeMethodData(Thread* t, object method, void* function) return data; } -inline object +inline void resolveNativeMethodData(Thread* t, object method) { if (methodCode(t, method) == 0) { @@ -468,8 +494,13 @@ resolveNativeMethodData(Thread* t, object method) if (LIKELY(p)) { PROTECT(t, method); object data = makeNativeMethodData(t, method, p); + + // ensure other threads see updated methodVmFlags before + // methodCode, and that the native method data is initialized + // before it is visible to those threads: + memoryBarrier(); + set(t, method, MethodCode, data); - return data; } else { object message = makeString (t, "%s.%s%s", @@ -477,11 +508,8 @@ resolveNativeMethodData(Thread* t, object method) &byteArrayBody(t, methodName(t, method), 0), &byteArrayBody(t, methodSpec(t, method), 0)); t->exception = makeUnsatisfiedLinkError(t, message); - return 0; } - } else { - return methodCode(t, method); - } + } } inline void @@ -498,35 +526,79 @@ checkStack(Thread* t, object method) } } -unsigned -invokeNative(Thread* t, object method) +void +pushResult(Thread* t, unsigned returnCode, uint64_t result, bool indirect) { - PROTECT(t, method); + switch (returnCode) { + case ByteField: + case BooleanField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; - object data = resolveNativeMethodData(t, method); - if (UNLIKELY(t->exception)) { - return VoidField; + case CharField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; + + case ShortField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, static_cast(result)); + break; + + case FloatField: + case IntField: + if (DebugRun) { + fprintf(stderr, "result: %d\n", static_cast(result)); + } + pushInt(t, result); + break; + + case LongField: + case DoubleField: + if (DebugRun) { + fprintf(stderr, "result: %"LLD"\n", result); + } + pushLong(t, result); + break; + + case ObjectField: + if (indirect) { + if (DebugRun) { + fprintf(stderr, "result: %p at %p\n", + static_cast(result) == 0 ? 0 : + *reinterpret_cast(static_cast(result)), + reinterpret_cast(static_cast(result))); + } + pushObject(t, static_cast(result) == 0 ? 0 : + *reinterpret_cast(static_cast(result))); + } else { + if (DebugRun) { + fprintf(stderr, "result: %p\n", reinterpret_cast(result)); + } + pushObject(t, reinterpret_cast(result)); + } + break; + + case VoidField: + break; + + default: + abort(t); } +} - PROTECT(t, data); - - pushFrame(t, method); - - unsigned count = nativeMethodDataLength(t, data) - 1; - - unsigned size = nativeMethodDataArgumentTableSize(t, data); - uintptr_t args[size / BytesPerWord]; +void +marshalArguments(Thread* t, uintptr_t* args, unsigned i, unsigned count, + object data, bool indirect) +{ unsigned offset = 0; - - args[offset++] = reinterpret_cast(t); - - unsigned i = 0; - if (methodFlags(t, method) & ACC_STATIC) { - ++ i; - args[offset++] = reinterpret_cast - (pushReference(t, methodClass(t, method))); - } - unsigned sp = frameBase(t, t->frame); for (; i < count; ++i) { unsigned type = nativeMethodDataParameterTypes(t, data, i + 1); @@ -548,16 +620,48 @@ invokeNative(Thread* t, object method) } break; case POINTER_TYPE: { - object* v = reinterpret_cast(t->stack + ((sp++) * 2) + 1); - if (*v == 0) { - v = 0; + if (indirect) { + object* v = reinterpret_cast(t->stack + ((sp++) * 2) + 1); + if (*v == 0) { + v = 0; + } + args[offset++] = reinterpret_cast(v); + } else { + args[offset++] = reinterpret_cast(peekObject(t, sp++)); } - args[offset++] = reinterpret_cast(v); } break; default: abort(t); } } +} + +unsigned +invokeNativeSlow(Thread* t, object method) +{ + PROTECT(t, method); + + object data = methodCode(t, method); + PROTECT(t, data); + + pushFrame(t, method); + + unsigned count = nativeMethodDataLength(t, data) - 1; + + unsigned size = nativeMethodDataArgumentTableSize(t, data); + uintptr_t args[size / BytesPerWord]; + unsigned offset = 0; + + args[offset++] = reinterpret_cast(t); + + unsigned i = 0; + if (methodFlags(t, method) & ACC_STATIC) { + ++ i; + args[offset++] = reinterpret_cast + (pushReference(t, methodClass(t, method))); + } + + marshalArguments(t, args + offset, i, count, data, true); unsigned returnCode = methodReturnCode(t, method); unsigned returnType = fieldType(t, returnCode); @@ -609,80 +713,67 @@ invokeNative(Thread* t, object method) return VoidField; } - switch (returnCode) { - case ByteField: - case BooleanField: - if (DebugRun) { - fprintf(stderr, "result: %d\n", static_cast(result)); - } - pushInt(t, static_cast(result)); - break; - - case CharField: - if (DebugRun) { - fprintf(stderr, "result: %d\n", static_cast(result)); - } - pushInt(t, static_cast(result)); - break; - - case ShortField: - if (DebugRun) { - fprintf(stderr, "result: %d\n", static_cast(result)); - } - pushInt(t, static_cast(result)); - break; - - case FloatField: - case IntField: - if (DebugRun) { - fprintf(stderr, "result: %d\n", static_cast(result)); - } - pushInt(t, result); - break; - - case LongField: - case DoubleField: - if (DebugRun) { - fprintf(stderr, "result: %"LLD"\n", result); - } - pushLong(t, result); - break; - - case ObjectField: - if (DebugRun) { - fprintf(stderr, "result: %p at %p\n", - static_cast(result) == 0 ? 0 : - *reinterpret_cast(static_cast(result)), - reinterpret_cast(static_cast(result))); - } - pushObject(t, static_cast(result) == 0 ? 0 : - *reinterpret_cast(static_cast(result))); - break; - - case VoidField: - break; - - default: - abort(t); - } + pushResult(t, returnCode, result, true); return returnCode; } +unsigned +invokeNative(Thread* t, object method) +{ + PROTECT(t, method); + + resolveNativeMethodData(t, method); + + if (UNLIKELY(t->exception)) { + return VoidField; + } + + if (methodVmFlags(t, method) & FastNative) { + pushFrame(t, method); + + object data = methodCode(t, method); + uintptr_t arguments[methodParameterFootprint(t, method)]; + marshalArguments + (t, arguments, (methodFlags(t, method) & ACC_STATIC) ? 1 : 0, + nativeMethodDataLength(t, data) - 1, data, false); + + uint64_t result = reinterpret_cast + (nativeMethodDataFunction(t, methodCode(t, method))) + (t, method, arguments); + + popFrame(t); + + if (UNLIKELY(t->exception)) { + return VoidField; + } + + pushResult(t, methodReturnCode(t, method), result, false); + + return methodReturnCode(t, method); + } else { + return invokeNativeSlow(t, method); + } +} + bool classInit2(Thread* t, object class_, unsigned ipOffset) { + for (ClassInitList* list = t->classInitList; list; list = list->next) { + if (list->class_ == class_) { + return false; + } + } + PROTECT(t, class_); - acquire(t, t->m->classLock); - if (classVmFlags(t, class_) & NeedInitFlag - and (classVmFlags(t, class_) & InitFlag) == 0) - { - classVmFlags(t, class_) |= InitFlag; + + if (preInitClass(t, class_)) { + ClassInitList::push(t, class_); + t->code = classInitializer(t, class_); t->ip -= ipOffset; return true; } else { - release(t, t->m->classLock); return false; } } @@ -911,7 +1002,7 @@ interpret(Thread* t) object class_ = resolveClassInPool(t, codePool(t, code), index - 1); if (UNLIKELY(exception)) goto throw_; - pushObject(t, makeObjectArray(t, class_, count, true)); + pushObject(t, makeObjectArray(t, class_, count)); } else { object message = makeString(t, "%d", count); exception = makeNegativeArraySizeException(t, message); @@ -2352,7 +2443,7 @@ interpret(Thread* t) } } - object array = makeArray(t, counts[0], true); + object array = makeArray(t, counts[0]); setObjectClass(t, array, class_); PROTECT(t, array); @@ -2383,35 +2474,35 @@ interpret(Thread* t) switch (type) { case T_BOOLEAN: - array = makeBooleanArray(t, count, true); + array = makeBooleanArray(t, count); break; case T_CHAR: - array = makeCharArray(t, count, true); + array = makeCharArray(t, count); break; case T_FLOAT: - array = makeFloatArray(t, count, true); + array = makeFloatArray(t, count); break; case T_DOUBLE: - array = makeDoubleArray(t, count, true); + array = makeDoubleArray(t, count); break; case T_BYTE: - array = makeByteArray(t, count, true); + array = makeByteArray(t, count); break; case T_SHORT: - array = makeShortArray(t, count, true); + array = makeShortArray(t, count); break; case T_INT: - array = makeIntArray(t, count, true); + array = makeIntArray(t, count); break; case T_LONG: - array = makeLongArray(t, count, true); + array = makeLongArray(t, count); break; default: abort(t); @@ -3001,7 +3092,7 @@ class MyProcessor: public Processor { return vm::makeClass (t, flags, vmFlags, arrayDimensions, fixedSize, arrayElementSize, objectMask, name, super, interfaceTable, virtualTable, fieldTable, - methodTable, staticTable, loader, 0, false); + methodTable, staticTable, loader, 0); } virtual void @@ -3010,22 +3101,6 @@ class MyProcessor: public Processor { // ignore } - virtual void - initClass(vm::Thread* t, object c) - { - PROTECT(t, c); - - acquire(t, t->m->classLock); - if (classVmFlags(t, c) & NeedInitFlag - and (classVmFlags(t, c) & InitFlag) == 0) - { - classVmFlags(t, c) |= InitFlag; - invoke(t, classInitializer(t, c), 0); - } else { - release(t, t->m->classLock); - } - } - virtual void visitObjects(vm::Thread* vmt, Heap::Visitor* v) { @@ -3038,6 +3113,10 @@ class MyProcessor: public Processor { v->visit(reinterpret_cast(t->stack + (i * 2) + 1)); } } + + for (ClassInitList* list = t->classInitList; list; list = list->next) { + v->visit(reinterpret_cast(&(list->class_))); + } } virtual void @@ -3157,25 +3236,21 @@ class MyProcessor: public Processor { return 0; } - virtual void compileThunks(vm::Thread*, BootImage*, uint8_t*, unsigned*, - unsigned) + virtual void initialize(BootImage*, uint8_t*, unsigned) { + abort(s); + } + + virtual void compileMethod(vm::Thread*, Zone*, object*, object*, + DelayedPromise**, object) { abort(s); } - virtual void compileMethod(vm::Thread*, Zone*, uint8_t*, unsigned*, unsigned, - object*, object*, DelayedPromise**, object) - { + virtual void visitRoots(HeapWalker*) { abort(s); } - virtual void visitRoots(BootImage*, HeapWalker*) { - abort(s); - } - - virtual unsigned* makeCallTable(vm::Thread*, BootImage*, HeapWalker*, - uint8_t*) - { + virtual unsigned* makeCallTable(vm::Thread*, HeapWalker*) { abort(s); } @@ -3183,6 +3258,29 @@ class MyProcessor: public Processor { expect(s, image == 0); } + + virtual void callWithCurrentContinuation(vm::Thread*, object) { + abort(s); + } + + virtual void dynamicWind(vm::Thread*, object, object, object) { + abort(s); + } + + virtual void feedResultToContinuation(vm::Thread*, object, object){ + abort(s); + } + + virtual void feedExceptionToContinuation(vm::Thread*, object, object) { + abort(s); + } + + virtual void walkContinuationBody(vm::Thread*, Heap::Walker*, object, + unsigned) + { + abort(s); + } + virtual void dispose(vm::Thread* t) { t->m->heap->free(t, sizeof(Thread)); } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 7a0b687c4f..ffd96025cc 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -99,6 +99,14 @@ GetEnv(Machine* m, Thread** t, jint version) } } +jsize JNICALL +GetVersion(Thread* t) +{ + ENTER(t, Thread::ActiveState); + + return JNI_VERSION_1_6; +} + jsize JNICALL GetStringLength(Thread* t, jstring s) { @@ -258,6 +266,26 @@ ExceptionCheck(Thread* t) return t->exception != 0; } +#ifndef AVIAN_GNU +jobject JNICALL +NewDirectByteBuffer(Thread*, void*, jlong) +{ + return 0; +} + +void* JNICALL +GetDirectBufferAddress(Thread*, jobject) +{ + return 0; +} + +jlong JNICALL +GetDirectBufferCapacity(Thread*, jobject) +{ + return -1; +} +#endif// not AVIAN_GNU + jclass JNICALL GetObjectClass(Thread* t, jobject o) { @@ -817,22 +845,12 @@ CallStaticVoidMethod(Thread* t, jclass c, jmethodID m, ...) va_end(a); } -object -findField(Thread* t, jclass c, const char* name, const char* spec) -{ - object n = makeByteArray(t, "%s", name); - PROTECT(t, n); - - object s = makeByteArray(t, "%s", spec); - return vm::findField(t, *c, n, s); -} - jfieldID JNICALL GetFieldID(Thread* t, jclass c, const char* name, const char* spec) { ENTER(t, Thread::ActiveState); - object field = findField(t, c, name, spec); + object field = resolveField(t, *c, name, spec); if (UNLIKELY(t->exception)) return 0; return fieldOffset(t, field); @@ -843,7 +861,7 @@ GetStaticFieldID(Thread* t, jclass c, const char* name, const char* spec) { ENTER(t, Thread::ActiveState); - object field = findField(t, c, name, spec); + object field = resolveField(t, *c, name, spec); if (UNLIKELY(t->exception)) return 0; return fieldOffset(t, field); @@ -998,7 +1016,7 @@ GetStaticObjectField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - return makeLocalReference(t, arrayBody(t, classStaticTable(t, *c), field)); + return makeLocalReference(t, cast(classStaticTable(t, *c), field)); } jboolean JNICALL @@ -1006,8 +1024,7 @@ GetStaticBooleanField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return v ? intValue(t, v) != 0 : false; + return cast(classStaticTable(t, *c), field); } jbyte JNICALL @@ -1015,8 +1032,7 @@ GetStaticByteField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return static_cast(v ? intValue(t, v) : 0); + return cast(classStaticTable(t, *c), field); } jchar JNICALL @@ -1024,8 +1040,7 @@ GetStaticCharField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return static_cast(v ? intValue(t, v) : 0); + return cast(classStaticTable(t, *c), field); } jshort JNICALL @@ -1033,8 +1048,7 @@ GetStaticShortField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return static_cast(v ? intValue(t, v) : 0); + return cast(classStaticTable(t, *c), field); } jint JNICALL @@ -1042,8 +1056,7 @@ GetStaticIntField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return v ? intValue(t, v) : 0; + return cast(classStaticTable(t, *c), field); } jlong JNICALL @@ -1051,8 +1064,7 @@ GetStaticLongField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - return static_cast(v ? longValue(t, v) : 0); + return cast(classStaticTable(t, *c), field); } jfloat JNICALL @@ -1060,10 +1072,7 @@ GetStaticFloatField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - jint i = v ? intValue(t, v) : 0; - jfloat f; memcpy(&f, &i, 4); - return f; + return cast(classStaticTable(t, *c), field); } jdouble JNICALL @@ -1071,10 +1080,7 @@ GetStaticDoubleField(Thread* t, jclass c, jfieldID field) { ENTER(t, Thread::ActiveState); - object v = arrayBody(t, classStaticTable(t, *c), field); - jlong i = v ? longValue(t, v) : 0; - jdouble f; memcpy(&f, &i, 4); - return f; + return cast(classStaticTable(t, *c), field); } void JNICALL @@ -1082,17 +1088,15 @@ SetStaticObjectField(Thread* t, jclass c, jfieldID field, jobject v) { ENTER(t, Thread::ActiveState); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), - (v ? *v : 0)); + set(t, classStaticTable(t, *c), field, (v ? *v : 0)); } void JNICALL SetStaticBooleanField(Thread* t, jclass c, jfieldID field, jboolean v) { ENTER(t, Thread::ActiveState); - - object o = makeInt(t, v ? 1 : 0); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1100,8 +1104,7 @@ SetStaticByteField(Thread* t, jclass c, jfieldID field, jbyte v) { ENTER(t, Thread::ActiveState); - object o = makeInt(t, v); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1109,8 +1112,7 @@ SetStaticCharField(Thread* t, jclass c, jfieldID field, jchar v) { ENTER(t, Thread::ActiveState); - object o = makeInt(t, v); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1118,8 +1120,7 @@ SetStaticShortField(Thread* t, jclass c, jfieldID field, jshort v) { ENTER(t, Thread::ActiveState); - object o = makeInt(t, v); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1127,8 +1128,7 @@ SetStaticIntField(Thread* t, jclass c, jfieldID field, jint v) { ENTER(t, Thread::ActiveState); - object o = makeInt(t, v); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1136,8 +1136,7 @@ SetStaticLongField(Thread* t, jclass c, jfieldID field, jlong v) { ENTER(t, Thread::ActiveState); - object o = makeLong(t, v); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1145,9 +1144,7 @@ SetStaticFloatField(Thread* t, jclass c, jfieldID field, jfloat v) { ENTER(t, Thread::ActiveState); - jint i; memcpy(&i, &v, 4); - object o = makeInt(t, i); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } void JNICALL @@ -1155,9 +1152,7 @@ SetStaticDoubleField(Thread* t, jclass c, jfieldID field, jdouble v) { ENTER(t, Thread::ActiveState); - jlong i; memcpy(&i, &v, 8); - object o = makeLong(t, i); - set(t, classStaticTable(t, *c), ArrayBody + (field * BytesPerWord), o); + cast(classStaticTable(t, *c), field) = v; } jobject JNICALL @@ -1883,6 +1878,17 @@ append(char** p, const char* value, unsigned length, char tail) namespace vm { +#ifdef AVIAN_GNU +jobject JNICALL +NewDirectByteBuffer(Thread*, void*, jlong); + +void* JNICALL +GetDirectBufferAddress(Thread*, jobject); + +jlong JNICALL +GetDirectBufferCapacity(Thread*, jobject); +#endif//AVIAN_GNU + void populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) { @@ -1895,6 +1901,7 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) memset(envTable, 0, sizeof(JNIEnvVTable)); + envTable->GetVersion = ::GetVersion; envTable->GetStringLength = ::GetStringLength; envTable->GetStringChars = ::GetStringChars; envTable->ReleaseStringChars = ::ReleaseStringChars; @@ -1907,6 +1914,9 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) envTable->FindClass = ::FindClass; envTable->ThrowNew = ::ThrowNew; envTable->ExceptionCheck = ::ExceptionCheck; + envTable->NewDirectByteBuffer = ::NewDirectByteBuffer; + envTable->GetDirectBufferAddress = ::GetDirectBufferAddress; + envTable->GetDirectBufferCapacity = ::GetDirectBufferCapacity; envTable->DeleteLocalRef = ::DeleteLocalRef; envTable->GetObjectClass = ::GetObjectClass; envTable->IsInstanceOf = ::IsInstanceOf; diff --git a/src/machine.cpp b/src/machine.cpp index 07ab7cb293..a31d3c016c 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -196,7 +196,7 @@ visitRoots(Thread* t, Heap::Visitor* v) } } -void +bool walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, unsigned arrayElementSize, unsigned arrayLength, unsigned start) { @@ -207,7 +207,7 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, for (unsigned i = start; i < fixedSizeInWords; ++i) { if (mask[i / 32] & (static_cast(1) << (i % 32))) { if (not w->visit(i)) { - return; + return false; } } } @@ -240,12 +240,31 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, if (not w->visit (fixedSizeInWords + (i * arrayElementSizeInWords) + j)) { - return; + return false; } } } } } + + return true; +} + +object +findInInterfaces(Thread* t, object class_, object name, object spec, + object (*find)(Thread*, object, object, object)) +{ + object result = 0; + if (classInterfaceTable(t, class_)) { + for (unsigned i = 0; + i < arrayLength(t, classInterfaceTable(t, class_)) and result == 0; + i += 2) + { + result = find + (t, arrayBody(t, classInterfaceTable(t, class_), i), name, spec); + } + } + return result; } void @@ -479,6 +498,33 @@ postCollect(Thread* t) } } +void +finalizeObject(Thread* t, object o) +{ + if (t->state == Thread::ExitState) { + // don't waste time running Java finalizers if we're exiting the + // VM + return; + } + + for (object c = objectClass(t, o); c; c = classSuper(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object m = arrayBody(t, classMethodTable(t, c), i); + + if (strcmp(reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, m), 0)) == 0 + and strcmp(reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, m), 0)) == 0) + { + t->m->processor->invoke(t, m, o); + t->exception = 0; + return; + } + } + } + abort(t); +} + object makeByteArray(Thread* t, const char* format, va_list a) { @@ -495,21 +541,31 @@ makeByteArray(Thread* t, const char* format, va_list a) } object -parseUtf8(Thread* t, Stream& s, unsigned length) +parseUtf8NonAscii(Thread* t, Stream& s, object bytesSoFar, unsigned byteCount, + unsigned sourceIndex, unsigned lastByteRead) { - object value = makeByteArray(t, length + 1); + PROTECT(t, bytesSoFar); + + unsigned length = byteArrayLength(t, bytesSoFar) - 1; + object value = makeCharArray(t, length + 1); + unsigned vi = 0; - for (unsigned si = 0; si < length; ++si) { - unsigned a = s.read1(); + for (; vi < byteCount; ++vi) { + charArrayBody(t, value, vi) = byteArrayBody(t, bytesSoFar, vi); + } + + unsigned a = lastByteRead; + unsigned si = sourceIndex; + while (true) { if (a & 0x80) { - // todo: handle non-ASCII characters properly if (a & 0x20) { // 3 bytes si += 2; assert(t, si < length); - /*unsigned b = */s.read1(); - /*unsigned c = */s.read1(); - byteArrayBody(t, value, vi++) = '_'; + unsigned b = s.read1(); + unsigned c = s.read1(); + charArrayBody(t, value, vi++) + = ((a & 0xf) << 12) | ((b & 0x3f) << 6) | (c & 0x3f); } else { // 2 bytes ++ si; @@ -517,9 +573,55 @@ parseUtf8(Thread* t, Stream& s, unsigned length) unsigned b = s.read1(); if (a == 0xC0 and b == 0x80) { + charArrayBody(t, value, vi++) = 0; + } else { + charArrayBody(t, value, vi++) = ((a & 0x1f) << 6) | (b & 0x3f); + } + } + } else { + charArrayBody(t, value, vi++) = a; + } + + if (++si < length) { + a = s.read1(); + } else { + break; + } + } + + if (vi < length) { + PROTECT(t, value); + + object v = makeCharArray(t, vi + 1); + memcpy(&charArrayBody(t, v, 0), &charArrayBody(t, value, 0), vi * 2); + value = v; + } + + charArrayBody(t, value, vi) = 0; + return value; +} + +object +parseUtf8(Thread* t, Stream& s, unsigned length) +{ + object value = makeByteArray(t, length + 1); + unsigned vi = 0; + for (unsigned si = 0; si < length; ++si) { + unsigned a = s.read1(); + if (a & 0x80) { + if (a & 0x20) { + // 3 bytes + return parseUtf8NonAscii(t, s, value, vi, si, a); + } else { + // 2 bytes + unsigned b = s.read1(); + + if (a == 0xC0 and b == 0x80) { + ++ si; + assert(t, si < length); byteArrayBody(t, value, vi++) = 0; } else { - byteArrayBody(t, value, vi++) = '_'; + return parseUtf8NonAscii(t, s, value, vi, si, a); } } } else { @@ -539,6 +641,28 @@ parseUtf8(Thread* t, Stream& s, unsigned length) return value; } +void +removeByteArray(Thread* t, object o) +{ + hashMapRemove(t, t->m->byteArrayMap, o, byteArrayHash, objectEqual); +} + +object +internByteArray(Thread* t, object array) +{ + PROTECT(t, array); + + object n = hashMapFindNode + (t, t->m->byteArrayMap, array, byteArrayHash, byteArrayEqual); + if (n) { + return jreferenceTarget(t, tripleFirst(t, n)); + } else { + hashMapInsert(t, t->m->byteArrayMap, array, 0, byteArrayHash); + addFinalizer(t, array, removeByteArray); + return array; + } +} + unsigned parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) { @@ -561,6 +685,11 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) case CONSTANT_Utf8: { if (singletonObject(t, pool, i) == 0) { object value = parseUtf8(t, s, s.read2()); + if (objectClass(t, value) + == arrayBody(t, t->m->types, Machine::ByteArrayType)) + { + value = internByteArray(t, value); + } set(t, pool, SingletonBody + (i * BytesPerWord), value); } } return 1; @@ -581,7 +710,8 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) parsePoolEntry(t, s, index, pool, si); object value = singletonObject(t, pool, si); - value = makeString(t, value, 0, byteArrayLength(t, value) - 1, 0); + value = makeString + (t, value, 0, cast(value, BytesPerWord) - 1, 0); value = intern(t, value); set(t, pool, SingletonBody + (i * BytesPerWord), value); } @@ -628,8 +758,13 @@ object parsePool(Thread* t, Stream& s) { unsigned count = s.read2() - 1; - - object pool = makeSingletonOfSize(t, count); + unsigned old; + unsigned floatMaskSize = 0; + do { + old = floatMaskSize; + floatMaskSize = singletonMaskSize(count + floatMaskSize); + } while (floatMaskSize != old); + object pool = makeSingletonOfSize(t, count + floatMaskSize); PROTECT(t, pool); if (count) { @@ -641,12 +776,18 @@ parsePool(Thread* t, Stream& s) switch (s.read1()) { case CONSTANT_Class: case CONSTANT_String: + assert(t, !singletonIsFloat(t, pool, i)); singletonMarkObject(t, pool, i); s.skip(2); break; case CONSTANT_Integer: + assert(t, !singletonIsFloat(t, pool, i)); + s.skip(4); + break; case CONSTANT_Float: + singletonMarkBit(t, pool, count, i); + assert(t, singletonIsFloat(t, pool, i)); s.skip(4); break; @@ -654,17 +795,27 @@ parsePool(Thread* t, Stream& s) case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: + assert(t, !singletonIsFloat(t, pool, i)); singletonMarkObject(t, pool, i); s.skip(4); break; case CONSTANT_Long: + assert(t, !singletonIsFloat(t, pool, i)); + s.skip(8); + ++ i; + break; case CONSTANT_Double: + singletonMarkBit(t, pool, count, i); + singletonMarkBit(t, pool, count, i + 1); + assert(t, singletonIsFloat(t, pool, i)); + assert(t, singletonIsFloat(t, pool, i + 1)); s.skip(8); ++ i; break; case CONSTANT_Utf8: + assert(t, !singletonIsFloat(t, pool, i)); singletonMarkObject(t, pool, i); s.skip(s.read2()); break; @@ -728,6 +879,8 @@ parseInterfaceTable(Thread* t, Stream& s, object class_, object pool) PROTECT(t, name); object interface = resolveClass(t, name); + if (UNLIKELY(t->exception)) return; + PROTECT(t, interface); hashMapInsertMaybe(t, map, name, interface, byteArrayHash, byteArrayEqual); @@ -1161,6 +1314,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) hashMapInsert(t, virtualMap, method, method, methodHash); } + + if (UNLIKELY(strcmp + (reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, method), 0)) == 0 + and strcmp + (reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, method), 0)) == 0 + and (not emptyMethod(t, method)))) + { + classVmFlags(t, class_) |= HasFinalizerFlag; + } } else { methodOffset(t, method) = i; @@ -1313,14 +1477,9 @@ updateBootstrapClass(Thread* t, object bootstrapClass, object class_) expect(t, classSuper(t, bootstrapClass) == classSuper(t, class_)); expect(t, bootstrapClass == arrayBody(t, t->m->types, Machine::ClassType) - or classFixedSize(t, bootstrapClass) == classFixedSize(t, class_)); + or classFixedSize(t, bootstrapClass) >= classFixedSize(t, class_)); - expect(t, - (classVmFlags(t, bootstrapClass) & ReferenceFlag) - or (classObjectMask(t, bootstrapClass) == 0 - and classObjectMask(t, class_) == 0) - or intArrayEqual(t, classObjectMask(t, bootstrapClass), - classObjectMask(t, class_))); + expect(t, (classVmFlags(t, class_) & HasFinalizerFlag) == 0); PROTECT(t, bootstrapClass); PROTECT(t, class_); @@ -1329,7 +1488,7 @@ updateBootstrapClass(Thread* t, object bootstrapClass, object class_) classVmFlags(t, bootstrapClass) &= ~BootstrapFlag; classVmFlags(t, bootstrapClass) |= classVmFlags(t, class_); - classFlags(t, bootstrapClass) = classFlags(t, class_); + classFlags(t, bootstrapClass) |= classFlags(t, class_); set(t, bootstrapClass, ClassSuper, classSuper(t, class_)); set(t, bootstrapClass, ClassInterfaceTable, classInterfaceTable(t, class_)); @@ -1518,7 +1677,7 @@ boot(Thread* t) m->unsafe = true; - m->loader = allocate(t, sizeof(void*) * 3, true); + m->loader = allocate(t, FixedSizeOfSystemClassLoader, true); m->types = allocate(t, pad((TypeCount + 2) * BytesPerWord), true); arrayLength(t, m->types) = TypeCount; @@ -1544,9 +1703,12 @@ boot(Thread* t) m->unsafe = false; - classVmFlags(t, arrayBody(t, m->types, Machine::SingletonType)) + classFlags(t, arrayBody(t, m->types, Machine::SingletonType)) |= SingletonFlag; + classFlags(t, arrayBody(t, m->types, Machine::ContinuationType)) + |= ContinuationFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JreferenceType)) |= ReferenceFlag; classVmFlags(t, arrayBody(t, m->types, Machine::WeakReferenceType)) @@ -1709,6 +1871,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, bootstrapClassMap(0), monitorMap(0), stringMap(0), + byteArrayMap(0), types(0), jniMethodTable(0), finalizers(0), @@ -1717,6 +1880,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, weakReferences(0), tenuredWeakReferences(0), unsafe(false), + triedBuiltinOnLoad(false), heapPoolIndex(0) { heap->setClient(heapClient); @@ -1779,6 +1943,7 @@ Thread::Thread(Machine* m, object javaThread, Thread* parent): heapIndex(0), heapOffset(0), protector(0), + classInitStack(0), runnable(this), defaultHeap(static_cast (m->heap->allocate(ThreadHeapSizeInBytes))), @@ -1829,6 +1994,7 @@ Thread::init() boot(this); } + m->byteArrayMap = makeWeakHashMap(this, 0, 0); m->monitorMap = makeWeakHashMap(this, 0, 0); m->jniMethodTable = makeVector(this, 0, 0); @@ -1842,8 +2008,12 @@ Thread::init() if (javaThread) { threadPeer(this, javaThread) = reinterpret_cast(this); } else { + const unsigned NewState = 0; + const unsigned NormalPriority = 5; + this->javaThread = makeThread - (this, reinterpret_cast(this), 0, 0, 0, 0, m->loader, 0); + (this, reinterpret_cast(this), 0, 0, NewState, NormalPriority, + 0, 0, 0, m->loader, 0, 0, 0); } } @@ -2057,44 +2227,53 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, // another thread wants to enter the exclusive state, either for a // collection or some other reason. We give it a chance here. ENTER(t, Thread::IdleState); + + while (t->m->exclusive) { + t->m->stateLock->wait(t->systemThread, 0); + } } - switch (type) { - case Machine::MovableAllocation: - if (t->heapIndex + ceiling(sizeInBytes, BytesPerWord) - > ThreadHeapSizeInWords) - { - t->heap = 0; - if (t->m->heapPoolIndex < ThreadHeapPoolSize) { - t->heap = static_cast - (t->m->heap->tryAllocate(ThreadHeapSizeInBytes)); + do { + switch (type) { + case Machine::MovableAllocation: + if (t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords) + { + t->heap = 0; + if (t->m->heapPoolIndex < ThreadHeapPoolSize) { + t->heap = static_cast + (t->m->heap->tryAllocate(ThreadHeapSizeInBytes)); - if (t->heap) { - memset(t->heap, 0, ThreadHeapSizeInBytes); + if (t->heap) { + memset(t->heap, 0, ThreadHeapSizeInBytes); - t->m->heapPool[t->m->heapPoolIndex++] = t->heap; - t->heapOffset += t->heapIndex; - t->heapIndex = 0; + t->m->heapPool[t->m->heapPoolIndex++] = t->heap; + t->heapOffset += t->heapIndex; + t->heapIndex = 0; + } } } + break; + + case Machine::FixedAllocation: + if (t->m->fixedFootprint + sizeInBytes > FixedFootprintThresholdInBytes) + { + t->heap = 0; + } + break; + + case Machine::ImmortalAllocation: + break; } - break; - case Machine::FixedAllocation: - if (t->m->fixedFootprint + sizeInBytes > FixedFootprintThresholdInBytes) { - t->heap = 0; + if (t->heap == 0) { + // fprintf(stderr, "gc"); + // vmPrintTrace(t); + collect(t, Heap::MinorCollection); } - break; - - case Machine::ImmortalAllocation: - break; - } - - if (t->heap == 0) { -// fprintf(stderr, "gc"); -// vmPrintTrace(t); - collect(t, Heap::MinorCollection); - } + } while (type == Machine::MovableAllocation + and t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + > ThreadHeapSizeInWords); switch (type) { case Machine::MovableAllocation: { @@ -2133,6 +2312,28 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, } } +object +makeNewGeneral(Thread* t, object class_) +{ + assert(t, t->state == Thread::ActiveState); + + object instance = makeNew(t, class_); + PROTECT(t, instance); + + if (classVmFlags(t, class_) & WeakReferenceFlag) { + ACQUIRE(t, t->m->referenceLock); + + jreferenceVmNext(t, instance) = t->m->weakReferences; + t->m->weakReferences = instance; + } + + if (classVmFlags(t, class_) & HasFinalizerFlag) { + addFinalizer(t, instance, finalizeObject); + } + + return instance; +} + object makeByteArray(Thread* t, const char* format, ...) { @@ -2399,7 +2600,8 @@ parseClass(Thread* t, const uint8_t* data, unsigned size) set(t, class_, ClassSuper, sc); classVmFlags(t, class_) - |= (classVmFlags(t, sc) & (ReferenceFlag | WeakReferenceFlag)); + |= (classVmFlags(t, sc) + & (ReferenceFlag | WeakReferenceFlag | HasFinalizerFlag)); } parseInterfaceTable(t, s, class_, pool); @@ -2432,6 +2634,8 @@ parseClass(Thread* t, const uint8_t* data, unsigned size) classLoader(t, class_), vtableLength); + PROTECT(t, real); + t->m->processor->initVtable(t, real); updateClassTables(t, real, class_); @@ -2510,24 +2714,58 @@ resolveClass(Thread* t, object spec) } object -resolveMethod(Thread* t, const char* className, const char* methodName, +resolveMethod(Thread* t, object class_, const char* methodName, const char* methodSpec) { - object class_ = resolveClass(t, makeByteArray(t, "%s", className)); - if (LIKELY(t->exception == 0)) { - PROTECT(t, class_); + PROTECT(t, class_); - object name = makeByteArray(t, methodName); - PROTECT(t, name); + object name = makeByteArray(t, methodName); + PROTECT(t, name); - object spec = makeByteArray(t, methodSpec); - object reference = makeReference(t, class_, name, spec); + object spec = makeByteArray(t, methodSpec); - return findMethodInClass(t, class_, referenceName(t, reference), - referenceSpec(t, reference)); + object method = findMethodInClass(t, class_, name, spec); + + if (t->exception == 0 and method == 0) { + object message = makeString + (t, "%s %s not found in %s", methodName, methodSpec, + &byteArrayBody(t, className(t, class_), 0)); + + t->exception = makeNoSuchMethodError(t, message); + return 0; + } else { + return method; + } +} + +object +resolveField(Thread* t, object class_, const char* fieldName, + const char* fieldSpec) +{ + PROTECT(t, class_); + + object name = makeByteArray(t, fieldName); + PROTECT(t, name); + + object spec = makeByteArray(t, fieldSpec); + PROTECT(t, spec); + + object field = findInInterfaces(t, class_, name, spec, findFieldInClass); + + for (; class_ != 0 and field == 0; class_ = classSuper(t, class_)) { + field = findFieldInClass(t, class_, name, spec); } - return 0; + if (t->exception == 0 and field == 0) { + object message = makeString + (t, "%s %s not found in %s", fieldName, fieldSpec, + &byteArrayBody(t, className(t, class_), 0)); + + t->exception = makeNoSuchFieldError(t, message); + return 0; + } else { + return field; + } } object @@ -2556,6 +2794,92 @@ resolveObjectArrayClass(Thread* t, object elementSpec) return resolveClass(t, spec); } +bool +classNeedsInit(Thread* t, object c) +{ + if (classVmFlags(t, c) & NeedInitFlag) { + if (classVmFlags(t, c) & InitFlag) { + // the class is currently being initialized. If this the thread + // which is initializing it, we should not try to initialize it + // recursively. Otherwise, we must wait for the responsible + // thread to finish. + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return false; + } + } + } + return true; + } else { + return false; + } +} + +bool +preInitClass(Thread* t, object c) +{ + if (classVmFlags(t, c) & NeedInitFlag) { + PROTECT(t, c); + ACQUIRE(t, t->m->classLock); + if (classVmFlags(t, c) & NeedInitFlag) { + if (classVmFlags(t, c) & InitFlag) { + // the class is currently being initialized. If this the + // thread which is initializing it, we should not try to + // initialize it recursively. + for (Thread::ClassInitStack* s = t->classInitStack; s; s = s->next) { + if (s->class_ == c) { + return false; + } + } + + // some other thread is on the job - wait for it to finish. + while (classVmFlags(t, c) & InitFlag) { + ENTER(t, Thread::IdleState); + t->m->classLock->wait(t->systemThread, 0); + } + } else if (classVmFlags(t, c) & InitErrorFlag) { + object message = makeString + (t, "%s", &byteArrayBody(t, className(t, c), 0)); + t->exception = makeNoClassDefFoundError(t, message); + } else { + classVmFlags(t, c) |= InitFlag; + return true; + } + } + } + return false; +} + +void +postInitClass(Thread* t, object c) +{ + PROTECT(t, c); + + ACQUIRE(t, t->m->classLock); + if (t->exception) { + t->exception = makeExceptionInInitializerError(t, t->exception); + classVmFlags(t, c) |= NeedInitFlag | InitErrorFlag; + classVmFlags(t, c) &= ~InitFlag; + } else { + classVmFlags(t, c) &= ~(NeedInitFlag | InitFlag); + } + t->m->classLock->notifyAll(t->systemThread); +} + +void +initClass(Thread* t, object c) +{ + PROTECT(t, c); + + if (preInitClass(t, c)) { + Thread::ClassInitStack stack(t, c); + + t->m->processor->invoke(t, classInitializer(t, c), 0); + + postInitClass(t, c); + } +} + object makeObjectArray(Thread* t, object elementClass, unsigned count) { @@ -2606,7 +2930,6 @@ findInHierarchy(Thread* t, object class_, object name, object spec, object (*makeError)(Thread*, object)) { object originalClass = class_; - PROTECT(t, class_); object o = 0; if ((classFlags(t, class_) & ACC_INTERFACE) @@ -2617,9 +2940,17 @@ findInHierarchy(Thread* t, object class_, object name, object spec, } if (o == 0) { + if (find == findFieldInClass) { + o = findInInterfaces(t, originalClass, name, spec, find); + } + for (; o == 0 and class_; class_ = classSuper(t, class_)) { o = find(t, class_, name, spec); } + + if (o == 0 and find == findMethodInClass) { + o = findInInterfaces(t, originalClass, name, spec, find); + } } if (o == 0) { @@ -2760,13 +3091,6 @@ collect(Thread* t, Heap::CollectionType type) postCollect(m->rootThread); - for (object f = m->finalizeQueue; f; f = finalizerNext(t, f)) { - void (*function)(Thread*, object); - memcpy(&function, &finalizerFinalize(t, f), BytesPerWord); - function(t, finalizerTarget(t, f)); - } - m->finalizeQueue = 0; - killZombies(t, m->rootThread); for (unsigned i = 0; i < m->heapPoolIndex; ++i) { @@ -2779,6 +3103,14 @@ collect(Thread* t, Heap::CollectionType type) #ifdef VM_STRESS if (not stress) t->stress = false; #endif + + object f = m->finalizeQueue; + m->finalizeQueue = 0; + for (; f; f = finalizerNext(t, f)) { + void (*function)(Thread*, object); + memcpy(&function, &finalizerFinalize(t, f), BytesPerWord); + function(t, finalizerTarget(t, f)); + } } void @@ -2788,6 +3120,8 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start) object objectMask = static_cast (t->m->heap->follow(classObjectMask(t, class_))); + bool more = true; + if (objectMask) { unsigned fixedSize = classFixedSize(t, class_); unsigned arrayElementSize = classArrayElementSize(t, class_); @@ -2799,17 +3133,21 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start) memcpy(mask, &intArrayBody(t, objectMask, 0), intArrayLength(t, objectMask) * 4); - ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); - } else if (classVmFlags(t, class_) & SingletonFlag) { + more = ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); + } else if (classFlags(t, class_) & SingletonFlag) { unsigned length = singletonLength(t, o); if (length) { - ::walk(t, w, singletonMask(t, o), - (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); + more = ::walk(t, w, singletonMask(t, o), + (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); } else if (start == 0) { - w->visit(0); + more = w->visit(0); } } else if (start == 0) { - w->visit(0); + more = w->visit(0); + } + + if (more and classFlags(t, class_) & ContinuationFlag) { + t->m->processor->walkContinuationBody(t, w, o, start); } } @@ -2840,16 +3178,17 @@ visitRoots(Machine* m, Heap::Visitor* v) v->visit(&(m->bootstrapClassMap)); v->visit(&(m->monitorMap)); v->visit(&(m->stringMap)); + v->visit(&(m->byteArrayMap)); v->visit(&(m->types)); v->visit(&(m->jniMethodTable)); - for (Reference* r = m->jniReferences; r; r = r->next) { - v->visit(&(r->target)); - } - for (Thread* t = m->rootThread; t; t = t->peer) { ::visitRoots(t, v); } + + for (Reference* r = m->jniReferences; r; r = r->next) { + v->visit(&(r->target)); + } } void @@ -2956,11 +3295,11 @@ makeTrace(Thread* t, Thread* target) void runJavaThread(Thread* t) { - object method = resolveMethod(t, "java/lang/Thread", "run", "()V"); + object method = resolveMethod + (t, "java/lang/Thread", "run", "(Ljava/lang/Thread;)V"); + if (t->exception == 0) { - t->m->processor->invoke - (t, findMethod(t, method, objectClass(t, t->javaThread)), - t->javaThread); + t->m->processor->invoke(t, method, 0, t->javaThread); } } diff --git a/src/machine.h b/src/machine.h index c60c4a0eda..d191024db8 100644 --- a/src/machine.h +++ b/src/machine.h @@ -74,20 +74,31 @@ enum StackTag { const int NativeLine = -1; const int UnknownLine = -2; -// class flags: +// class flags (note that we must be careful not to overlap the +// standard ACC_* flags): +const unsigned HasFinalMemberFlag = 1 << 13; +const unsigned SingletonFlag = 1 << 14; +const unsigned ContinuationFlag = 1 << 15; + +// class vmFlags: const unsigned ReferenceFlag = 1 << 0; const unsigned WeakReferenceFlag = 1 << 1; const unsigned NeedInitFlag = 1 << 2; const unsigned InitFlag = 1 << 3; -const unsigned PrimitiveFlag = 1 << 4; -const unsigned SingletonFlag = 1 << 5; +const unsigned InitErrorFlag = 1 << 4; +const unsigned PrimitiveFlag = 1 << 5; const unsigned BootstrapFlag = 1 << 6; -const unsigned HasFinalMemberFlag = 1 << 7; +const unsigned HasFinalizerFlag = 1 << 7; -// method flags: +// method vmFlags: const unsigned ClassInitFlag = 1 << 0; const unsigned CompiledFlag = 1 << 1; const unsigned ConstructorFlag = 1 << 2; +const unsigned FastNative = 1 << 3; + +#ifndef JNI_VERSION_1_6 +#define JNI_VERSION_1_6 0x00010006 +#endif typedef Machine JavaVM; typedef Thread JNIEnv; @@ -1181,6 +1192,7 @@ class Machine { object bootstrapClassMap; object monitorMap; object stringMap; + object byteArrayMap; object types; object jniMethodTable; object finalizers; @@ -1189,6 +1201,7 @@ class Machine { object weakReferences; object tenuredWeakReferences; bool unsafe; + bool triedBuiltinOnLoad; JavaVMVTable javaVMVTable; JNIEnvVTable jniEnvVTable; uintptr_t* heapPool[ThreadHeapPoolSize]; @@ -1256,6 +1269,25 @@ class Thread { object* p; }; + class ClassInitStack { + public: + ClassInitStack(Thread* t, object class_): + next(t->classInitStack), + class_(class_), + protector(t, &(this->class_)) + { + t->classInitStack = this; + } + + ~ClassInitStack() { + protector.t->classInitStack = next; + } + + ClassInitStack* next; + object class_; + SingleProtector protector; + }; + class Runnable: public System::Runnable { public: Runnable(Thread* t): t(t) { } @@ -1308,6 +1340,7 @@ class Thread { unsigned heapIndex; unsigned heapOffset; Protector* protector; + ClassInitStack* classInitStack; Runnable runnable; uintptr_t* defaultHeap; uintptr_t* heap; @@ -1320,6 +1353,8 @@ class Thread { #endif // VM_STRESS }; +typedef uint64_t (JNICALL *FastNativeFunction)(Thread*, object, uintptr_t*); + inline object objectClass(Thread*, object o) { @@ -1454,17 +1489,17 @@ expect(Thread* t, bool v) class FixedAllocator: public Allocator { public: - FixedAllocator(Thread* t, uint8_t* base, unsigned capacity): - t(t), base(base), offset(0), capacity(capacity) + FixedAllocator(System* s, uint8_t* base, unsigned capacity): + s(s), base(base), offset(0), capacity(capacity) { } virtual void* tryAllocate(unsigned) { - abort(t); + abort(s); } virtual void* allocate(unsigned size) { unsigned paddedSize = pad(size); - expect(t, offset + paddedSize < capacity); + expect(s, offset + paddedSize < capacity); void* p = base + offset; offset += paddedSize; @@ -1472,10 +1507,10 @@ class FixedAllocator: public Allocator { } virtual void free(const void*, unsigned) { - abort(t); + abort(s); } - Thread* t; + System* s; uint8_t* base; unsigned offset; unsigned capacity; @@ -1508,6 +1543,9 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, inline object allocateSmall(Thread* t, unsigned sizeInBytes) { + assert(t, t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + <= ThreadHeapSizeInWords); + object o = reinterpret_cast(t->heap + t->heapIndex); t->heapIndex += ceiling(sizeInBytes, BytesPerWord); cast(o, 0) = 0; @@ -1696,7 +1734,7 @@ makeClassNotFoundException(Thread* t, object message) { PROTECT(t, message); object trace = makeTrace(t); - return makeClassNotFoundException(t, message, trace, 0); + return makeClassNotFoundException(t, message, trace, 0, 0); } inline object @@ -1719,6 +1757,12 @@ makeInterruptedException(Thread* t) return makeInterruptedException(t, 0, makeTrace(t), 0); } +inline object +makeIncompatibleContinuationException(Thread* t) +{ + return makeIncompatibleContinuationException(t, 0, makeTrace(t), 0); +} + inline object makeStackOverflowError(Thread* t) { @@ -1741,6 +1785,14 @@ makeNoSuchMethodError(Thread* t, object message) return makeNoSuchMethodError(t, message, trace, 0); } +inline object +makeNoClassDefFoundError(Thread* t, object message) +{ + PROTECT(t, message); + object trace = makeTrace(t); + return makeNoClassDefFoundError(t, message, trace, 0); +} + inline object makeUnsatisfiedLinkError(Thread* t, object message) { @@ -1754,7 +1806,7 @@ makeExceptionInInitializerError(Thread* t, object cause) { PROTECT(t, cause); object trace = makeTrace(t); - return makeExceptionInInitializerError(t, 0, trace, cause); + return makeExceptionInInitializerError(t, 0, trace, cause, cause); } inline object @@ -1770,27 +1822,16 @@ makeNew(Thread* t, object class_) return instance; } -inline object -makeNewWeakReference(Thread* t, object class_) -{ - assert(t, t->state == Thread::ActiveState); - - object instance = makeNew(t, class_); - PROTECT(t, instance); - - ACQUIRE(t, t->m->referenceLock); - - jreferenceVmNext(t, instance) = t->m->weakReferences; - t->m->weakReferences = instance; - - return instance; -} +object +makeNewGeneral(Thread* t, object class_); inline object make(Thread* t, object class_) { - if (UNLIKELY(classVmFlags(t, class_) & WeakReferenceFlag)) { - return makeNewWeakReference(t, class_); + if (UNLIKELY(classVmFlags(t, class_) + & (WeakReferenceFlag | HasFinalizerFlag))) + { + return makeNewGeneral(t, class_); } else { return makeNew(t, class_); } @@ -1913,9 +1954,9 @@ stringCharAt(Thread* t, object s, int i) if (objectClass(t, data) == arrayBody(t, t->m->types, Machine::ByteArrayType)) { - return byteArrayBody(t, data, i); + return byteArrayBody(t, data, stringOffset(t, s) + i); } else { - return charArrayBody(t, data, i); + return charArrayBody(t, data, stringOffset(t, s) + i); } } @@ -1936,15 +1977,6 @@ stringEqual(Thread* t, object a, object b) } } -inline bool -intArrayEqual(Thread* t, object a, object b) -{ - return a == b or - ((intArrayLength(t, a) == intArrayLength(t, b)) and - memcmp(&intArrayBody(t, a, 0), &intArrayBody(t, b, 0), - intArrayLength(t, a) * 4) == 0); -} - inline uint32_t methodHash(Thread* t, object method) { @@ -2040,33 +2072,72 @@ fieldSize(Thread* t, object field) object findLoadedClass(Thread* t, object spec); +inline bool +emptyMethod(Thread* t, object method) +{ + return ((methodFlags(t, method) & ACC_NATIVE) == 0) + and (codeLength(t, methodCode(t, method)) == 1) + and (codeBody(t, methodCode(t, method), 0) == return_); +} + object parseClass(Thread* t, const uint8_t* data, unsigned length); object -resolveClass(Thread* t, object spec); +resolveClass(Thread* t, object name); + +inline object +resolveClass(Thread* t, const char* name) +{ + return resolveClass(t, makeByteArray(t, "%s", name)); +} object -resolveMethod(Thread* t, const char* className, const char* methodName, +resolveMethod(Thread* t, object class_, const char* methodName, const char* methodSpec); +inline object +resolveMethod(Thread* t, const char* className, const char* methodName, + const char* methodSpec) +{ + object class_ = resolveClass(t, className); + if (LIKELY(t->exception == 0)) { + return resolveMethod(t, class_, methodName, methodSpec); + } else { + return 0; + } +} + +object +resolveField(Thread* t, object class_, const char* fieldName, + const char* fieldSpec); + +inline object +resolveField(Thread* t, const char* className, const char* fieldName, + const char* fieldSpec) +{ + object class_ = resolveClass(t, className); + if (LIKELY(t->exception == 0)) { + return resolveField(t, class_, fieldName, fieldSpec); + } else { + return 0; + } +} + object resolveObjectArrayClass(Thread* t, object elementSpec); -inline bool -classNeedsInit(Thread* t, object c) -{ - return classVmFlags(t, c) & NeedInitFlag - and (classVmFlags(t, c) & InitFlag) == 0; -} +bool +classNeedsInit(Thread* t, object c); -inline void -initClass(Thread* t, object c) -{ - if (classNeedsInit(t, c)) { - t->m->processor->initClass(t, c); - } -} +bool +preInitClass(Thread* t, object c); + +void +postInitClass(Thread* t, object c); + +void +initClass(Thread* t, object c); object makeObjectArray(Thread* t, object elementClass, unsigned count); @@ -2095,13 +2166,6 @@ findInHierarchy(Thread* t, object class_, object name, object spec, object (*find)(Thread*, object, object, object), object (*makeError)(Thread*, object)); -inline object -findField(Thread* t, object class_, object name, object spec) -{ - return findInHierarchy - (t, class_, name, spec, findFieldInClass, makeNoSuchFieldError); -} - inline object findMethod(Thread* t, object class_, object name, object spec) { @@ -2369,6 +2433,26 @@ makeSingletonOfSize(Thread* t, unsigned count) return o; } +inline void +singletonMarkBit(Thread* t, object singleton, unsigned start, unsigned index) +{ + uintptr_t& val = singletonValue(t, singleton, start + (index / BitsPerWord)); + val |= static_cast(1) << (index % BitsPerWord); +} + +inline bool +singletonGetBit(Thread* t, object singleton, unsigned start, unsigned index) +{ + uintptr_t& val = singletonValue(t, singleton, start + (index / BitsPerWord)); + return (val & static_cast(1) << (index % BitsPerWord)) != 0; +} + +inline bool +singletonIsFloat(Thread* t, object singleton, unsigned index) +{ + return singletonGetBit(t, singleton, singletonLength(t, singleton) - 2 * singletonMaskSize(t, singleton), index); +} + void dumpHeap(Thread* t, FILE* out); diff --git a/src/main.cpp b/src/main.cpp index 5e412c0bae..074297016d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -83,6 +83,10 @@ main(int ac, const char** av) ++ vmArgs.nOptions; #endif +#ifdef BOOT_BUILTINS + ++ vmArgs.nOptions; +#endif + JavaVMOption options[vmArgs.nOptions]; vmArgs.options = options; @@ -103,6 +107,11 @@ main(int ac, const char** av) = const_cast("-Davian.bootstrap=" BOOT_LIBRARY); #endif +#ifdef BOOT_BUILTINS + options[optionIndex++].optionString + = const_cast("-Davian.builtins=" BOOT_BUILTINS); +#endif + #define CLASSPATH_PROPERTY "-Djava.class.path=" unsigned classpathSize = strlen(classpath); diff --git a/src/posix.cpp b/src/posix.cpp index bc50ed9439..48096dac52 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -63,8 +63,6 @@ const unsigned VisitSignalIndex = 0; const unsigned SegFaultSignalIndex = 1; const unsigned InterruptSignalIndex = 2; -const unsigned ExecutableAreaSizeInBytes = 16 * 1024 * 1024; - class MySystem; MySystem* system; @@ -126,9 +124,7 @@ class MySystem: public System { r->setInterrupted(true); - if (flags & Waiting) { - pthread_kill(thread, InterruptSignal); - } + pthread_kill(thread, InterruptSignal); } virtual void join() { @@ -518,9 +514,7 @@ class MySystem: public System { MySystem(): threadVisitor(0), - visitTarget(0), - executableArea(0), - executableOffset(0) + visitTarget(0) { expect(this, system == 0); system = this; @@ -559,33 +553,23 @@ class MySystem: public System { } virtual void* tryAllocateExecutable(unsigned sizeInBytes) { - if (executableArea == 0) { #ifdef __x86_64__ - const unsigned Extra = MAP_32BIT; + const unsigned Extra = MAP_32BIT; #else - const unsigned Extra = 0; + const unsigned Extra = 0; #endif - void* p = mmap(0, ExecutableAreaSizeInBytes, PROT_EXEC | PROT_READ - | PROT_WRITE, MAP_PRIVATE | MAP_ANON | Extra, -1, 0); + void* p = mmap(0, sizeInBytes, PROT_EXEC | PROT_READ + | PROT_WRITE, MAP_PRIVATE | MAP_ANON | Extra, -1, 0); - if (p != MAP_FAILED) { - executableArea = static_cast(p); - } - } - - if (executableArea - and executableOffset + pad(sizeInBytes) < ExecutableAreaSizeInBytes) - { - void* r = executableArea + executableOffset; - executableOffset += pad(sizeInBytes); - return r; - } else { + if (p == MAP_FAILED) { return 0; + } else { + return static_cast(p); } } - virtual void freeExecutable(const void*, unsigned) { - // ignore + virtual void freeExecutable(const void* p, unsigned sizeInBytes) { + munmap(const_cast(p), sizeInBytes); } virtual bool success(Status s) { @@ -642,13 +626,16 @@ class MySystem: public System { visitTarget = target; int rv = pthread_kill(target->thread, VisitSignal); - expect(this, rv == 0); - while (visitTarget) visitLock->wait(t, 0); + if (rv == 0) { + while (visitTarget) visitLock->wait(t, 0); - threadVisitor = 0; + threadVisitor = 0; - return 0; + return 0; + } else { + return -1; + } } virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types, @@ -780,11 +767,6 @@ class MySystem: public System { registerHandler(0, VisitSignalIndex); system = 0; - if (executableArea) { - int r UNUSED = munmap(executableArea, ExecutableAreaSizeInBytes); - assert(this, r == 0); - } - ::free(this); } @@ -850,7 +832,7 @@ handleSignal(int signal, siginfo_t* info, void* context) sigaddset(&set, SegFaultSignal); sigprocmask(SIG_UNBLOCK, &set, 0); - vmJump(ip, base, stack, thread); + vmJump(ip, base, stack, thread, 0, 0); } } break; @@ -868,6 +850,7 @@ handleSignal(int signal, siginfo_t* info, void* context) } else { switch (signal) { case VisitSignal: + case InterruptSignal: break; default: diff --git a/src/powerpc.S b/src/powerpc.S index fba6f52ec0..fce2079af7 100644 --- a/src/powerpc.S +++ b/src/powerpc.S @@ -161,4 +161,6 @@ vmJump: mtlr r3 mr r1,r5 mr r13,r6 + mr r4,r7 + mr r3,r8 blr diff --git a/src/powerpc.cpp b/src/powerpc.cpp index da368961b7..99e0c0be34 100644 --- a/src/powerpc.cpp +++ b/src/powerpc.cpp @@ -65,6 +65,7 @@ inline int sth(int rs, int ra, int i) { return D(44, rs, ra, i); } inline int sthx(int rs, int ra, int rb) { return X(31, rs, ra, rb, 407, 0); } inline int stw(int rs, int ra, int i) { return D(36, rs, ra, i); } inline int stwu(int rs, int ra, int i) { return D(37, rs, ra, i); } +inline int stwux(int rs, int ra, int rb) { return X(31, rs, ra, rb, 183, 0); } inline int stwx(int rs, int ra, int rb) { return X(31, rs, ra, rb, 151, 0); } inline int add(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 266, 0); } inline int addc(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 10, 0); } @@ -136,7 +137,7 @@ inline int blt(int i) { return bc(12, 0, i, 0); } inline int bgt(int i) { return bc(12, 1, i, 0); } inline int bge(int i) { return bc(4, 0, i, 0); } inline int ble(int i) { return bc(4, 1, i, 0); } -inline int be(int i) { return bc(12, 2, i, 0); } +inline int beq(int i) { return bc(12, 2, i, 0); } inline int bne(int i) { return bc(4, 2, i, 0); } inline int cmpw(int ra, int rb) { return cmp(0, ra, rb); } inline int cmplw(int ra, int rb) { return cmpl(0, ra, rb); } @@ -158,6 +159,9 @@ carry16(intptr_t v) const unsigned FrameFooterSize = 6; +const unsigned StackAlignmentInBytes = 16; +const unsigned StackAlignmentInWords = StackAlignmentInBytes / BytesPerWord; + const int StackRegister = 1; const int ThreadRegister = 13; @@ -905,9 +909,15 @@ moveAndUpdateRM(Context* c, unsigned srcSize UNUSED, Assembler::Register* src, { assert(c, srcSize == BytesPerWord); assert(c, dstSize == BytesPerWord); - assert(c, dst->index == NoRegister); - issue(c, stwu(src->low, dst->base, dst->offset)); + if (dst->index == NoRegister) { + issue(c, stwu(src->low, dst->base, dst->offset)); + } else { + assert(c, dst->offset == 0); + assert(c, dst->scale == 1); + + issue(c, stwux(src->low, dst->base, dst->index)); + } } void @@ -1498,7 +1508,7 @@ jumpIfEqualC(Context* c, unsigned size UNUSED, Assembler::Constant* target) assert(c, size == BytesPerWord); appendOffsetTask(c, target->value, offset(c), true); - issue(c, be(0)); + issue(c, beq(0)); } void @@ -1588,6 +1598,9 @@ populateTables(ArchitectureContext* c) uo[index(Jump, R)] = CAST1(jumpR); uo[index(Jump, C)] = CAST1(jumpC); + uo[index(AlignedJump, R)] = CAST1(jumpR); + uo[index(AlignedJump, C)] = CAST1(jumpC); + uo[index(JumpIfEqual, C)] = CAST1(jumpIfEqualC); uo[index(JumpIfNotEqual, C)] = CAST1(jumpIfNotEqualC); uo[index(JumpIfGreater, C)] = CAST1(jumpIfGreaterC); @@ -1679,6 +1692,14 @@ class MyArchitecture: public Assembler::Architecture { return (BytesPerWord == 4 ? 3 : NoRegister); } + virtual int virtualCallTarget() { + return 4; + } + + virtual int virtualCallIndex() { + return 3; + } + virtual bool bigEndian() { return true; } @@ -1695,8 +1716,12 @@ class MyArchitecture: public Assembler::Architecture { } } + virtual unsigned frameFootprint(unsigned footprint) { + return max(footprint, StackAlignmentInWords); + } + virtual unsigned argumentFootprint(unsigned footprint) { - return footprint; + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); } virtual unsigned argumentRegisterCount() { @@ -1708,6 +1733,30 @@ class MyArchitecture: public Assembler::Architecture { return index + 3; } + + virtual uint64_t generalRegisters() { + return (static_cast(1) << 32) - 1; + } + + virtual uint64_t floatRegisters() { + return 0; + } + + virtual uint64_t allRegisters() { + return generalRegisters() | floatRegisters(); + } + + virtual unsigned stackAlignmentInWords() { + return StackAlignmentInWords; + } + + virtual bool matchCall(void* returnAddress, void* target) { + uint32_t* instruction = static_cast(returnAddress) - 1; + + return *instruction == static_cast + (bl(static_cast(target) + - reinterpret_cast(instruction))); + } virtual void updateCall(UnaryOperation op UNUSED, bool assertAlignment UNUSED, void* returnAddress, @@ -1730,6 +1779,10 @@ class MyArchitecture: public Assembler::Architecture { } } + virtual unsigned constantCallSize() { + return 4; + } + virtual uintptr_t getConstant(const void* src) { const int32_t* p = static_cast(src); return (p[0] << 16) | (p[1] & 0xFFFF); @@ -1740,7 +1793,7 @@ class MyArchitecture: public Assembler::Architecture { } virtual unsigned alignFrameSize(unsigned sizeInWords) { - const unsigned alignment = 16 / BytesPerWord; + const unsigned alignment = StackAlignmentInBytes / BytesPerWord; return (ceiling(sizeInWords + FrameFooterSize, alignment) * alignment); } @@ -1760,6 +1813,14 @@ class MyArchitecture: public Assembler::Architecture { return FrameFooterSize; } + virtual int returnAddressOffset() { + return 8 / BytesPerWord; + } + + virtual int framePointerOffset() { + return 0; + } + virtual void nextFrame(void** stack, void**) { assert(&c, *static_cast(*stack) != *stack); @@ -1778,6 +1839,14 @@ class MyArchitecture: public Assembler::Architecture { return false; } + virtual bool alwaysCondensed(BinaryOperation op) { + return false; + } + + virtual bool alwaysCondensed(TernaryOperation op) { + return false; + } + virtual void plan (UnaryOperation, unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, @@ -1898,8 +1967,8 @@ class MyArchitecture: public Assembler::Architecture { unsigned, const uint8_t* bTypeMask, const uint64_t* bRegisterMask, unsigned, uint8_t* cTypeMask, uint64_t* cRegisterMask) { - *cTypeMask = *bTypeMask; - *cRegisterMask = *bRegisterMask; + *cTypeMask = (1 << RegisterOperand); + *cRegisterMask = ~static_cast(0); } virtual void acquire() { @@ -1990,18 +2059,110 @@ class MyAssembler: public Assembler { moveAndUpdateRM(&c, BytesPerWord, &stack, BytesPerWord, &stackDst); } + virtual void adjustFrame(unsigned footprint) { + Register nextStack(0); + Memory stackSrc(StackRegister, 0); + moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &nextStack); + + Memory stackDst(StackRegister, -footprint * BytesPerWord); + moveAndUpdateRM(&c, BytesPerWord, &nextStack, BytesPerWord, &stackDst); + } + virtual void popFrame() { Register stack(StackRegister); Memory stackSrc(StackRegister, 0); moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &stack); - Assembler::Register returnAddress(0); - Assembler::Memory returnAddressSrc(StackRegister, 8); + Register returnAddress(0); + Memory returnAddressSrc(StackRegister, 8); moveMR(&c, BytesPerWord, &returnAddressSrc, BytesPerWord, &returnAddress); issue(&c, mtlr(returnAddress.low)); } + virtual void popFrameForTailCall(unsigned footprint, + int offset, + int returnAddressSurrogate, + int framePointerSurrogate) + { + if (TailCalls) { + if (offset) { + Register tmp(0); + Memory returnAddressSrc(StackRegister, 8 + (footprint * BytesPerWord)); + moveMR(&c, BytesPerWord, &returnAddressSrc, BytesPerWord, &tmp); + + issue(&c, mtlr(tmp.low)); + + Memory stackSrc(StackRegister, footprint * BytesPerWord); + moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &tmp); + + Memory stackDst(StackRegister, (footprint - offset) * BytesPerWord); + moveAndUpdateRM(&c, BytesPerWord, &tmp, BytesPerWord, &stackDst); + + if (returnAddressSurrogate != NoRegister) { + assert(&c, offset > 0); + + Register ras(returnAddressSurrogate); + Memory dst(StackRegister, 8 + (offset * BytesPerWord)); + moveRM(&c, BytesPerWord, &ras, BytesPerWord, &dst); + } + + if (framePointerSurrogate != NoRegister) { + assert(&c, offset > 0); + + Register fps(framePointerSurrogate); + Memory dst(StackRegister, offset * BytesPerWord); + moveRM(&c, BytesPerWord, &fps, BytesPerWord, &dst); + } + } else { + popFrame(); + } + } else { + abort(&c); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned argumentFootprint) { + popFrame(); + + assert(&c, argumentFootprint >= StackAlignmentInWords); + assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); + + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + Register tmp(0); + Memory stackSrc(StackRegister, 0); + moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &tmp); + + Memory stackDst(StackRegister, + (argumentFootprint - StackAlignmentInWords) + * BytesPerWord); + moveAndUpdateRM(&c, BytesPerWord, &tmp, BytesPerWord, &stackDst); + } + + return_(&c); + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned stackOffsetFromThread) + { + popFrame(); + + Register tmp1(0); + Memory stackSrc(StackRegister, 0); + moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &tmp1); + + Register tmp2(5); + Memory newStackSrc(ThreadRegister, stackOffsetFromThread); + moveMR(&c, BytesPerWord, &newStackSrc, BytesPerWord, &tmp2); + + Register stack(StackRegister); + subR(&c, BytesPerWord, &stack, &tmp2, &tmp2); + + Memory stackDst(StackRegister, 0, tmp2.low); + moveAndUpdateRM(&c, BytesPerWord, &tmp1, BytesPerWord, &stackDst); + + return_(&c); + } + virtual void apply(Operation op) { arch_->c.operations[op](&c); } diff --git a/src/process.cpp b/src/process.cpp index c2693d0281..a2aad7cd0d 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -67,7 +67,7 @@ mangle(int8_t c, char* dst) unsigned jniNameLength(Thread* t, object method, bool decorate) { - unsigned size = 5; + unsigned size = 0; object className = ::className(t, methodClass(t, method)); for (unsigned i = 0; i < byteArrayLength(t, className) - 1; ++i) { @@ -96,10 +96,11 @@ jniNameLength(Thread* t, object method, bool decorate) } void -makeJNIName(Thread* t, char* name, object method, bool decorate) +makeJNIName(Thread* t, const char* prefix, unsigned prefixLength, char* name, + object method, bool decorate) { - memcpy(name, "Java_", 5); - name += 5; + memcpy(name, prefix, prefixLength); + name += prefixLength; object className = ::className(t, methodClass(t, method)); for (unsigned i = 0; i < byteArrayLength(t, className) - 1; ++i) { @@ -146,31 +147,30 @@ resolveNativeMethod(Thread* t, const char* undecorated, const char* decorated) return 0; } -} // namespace - -namespace vm { - void* -resolveNativeMethod2(Thread* t, object method) +resolveNativeMethod(Thread* t, object method, const char* prefix, + unsigned prefixLength, int footprint UNUSED) { - unsigned undecoratedSize = jniNameLength(t, method, false); + unsigned undecoratedSize = prefixLength + jniNameLength(t, method, false); char undecorated[undecoratedSize + 1 + 6]; // extra 6 is for code below - makeJNIName(t, undecorated + 1, method, false); + makeJNIName(t, prefix, prefixLength, undecorated + 1, method, false); - unsigned decoratedSize = jniNameLength(t, method, true); + unsigned decoratedSize = prefixLength + jniNameLength(t, method, true); char decorated[decoratedSize + 1 + 6]; // extra 6 is for code below - makeJNIName(t, decorated + 1, method, true); + makeJNIName(t, prefix, prefixLength, decorated + 1, method, true); - void* p = ::resolveNativeMethod(t, undecorated + 1, decorated + 1); + void* p = resolveNativeMethod(t, undecorated + 1, decorated + 1); if (p) { return p; } #ifdef __MINGW32__ // on windows, we also try the _%s@%d and %s@%d variants - unsigned footprint = methodParameterFootprint(t, method) + 1; - if (methodFlags(t, method) & ACC_STATIC) { - ++ footprint; + if (footprint == -1) { + footprint = methodParameterFootprint(t, method) + 1; + if (methodFlags(t, method) & ACC_STATIC) { + ++ footprint; + } } *undecorated = '_'; @@ -181,13 +181,13 @@ resolveNativeMethod2(Thread* t, object method) snprintf(decorated + decoratedSize + 1, 5, "@%d", footprint * BytesPerWord); - p = ::resolveNativeMethod(t, undecorated, decorated); + p = resolveNativeMethod(t, undecorated, decorated); if (p) { return p; } // one more try without the leading underscore - p = ::resolveNativeMethod(t, undecorated + 1, decorated + 1); + p = resolveNativeMethod(t, undecorated + 1, decorated + 1); if (p) { return p; } @@ -196,6 +196,27 @@ resolveNativeMethod2(Thread* t, object method) return 0; } +} // namespace + +namespace vm { + +void* +resolveNativeMethod(Thread* t, object method) +{ + void* p = ::resolveNativeMethod(t, method, "Java_", 5, -1); + if (p) { + return p; + } + + p = ::resolveNativeMethod(t, method, "Avian_", 6, 3); + if (p) { + methodVmFlags(t, method) |= FastNative; + return p; + } + + return 0; +} + int findLineNumber(Thread* t, object method, unsigned ip) { diff --git a/src/process.h b/src/process.h index 8cbacf48b5..ad58e22c67 100644 --- a/src/process.h +++ b/src/process.h @@ -126,17 +126,7 @@ isSpecialMethod(Thread* t, object method, object class_) } void* -resolveNativeMethod2(Thread* t, object method); - -inline void* -resolveNativeMethod(Thread* t, object method) -{ - if (methodCode(t, method)) { - return pointerValue(t, methodCode(t, method)); - } else { - return resolveNativeMethod2(t, method); - } -} +resolveNativeMethod(Thread* t, object method); inline object findInterfaceMethod(Thread* t, object method, object class_) diff --git a/src/processor.h b/src/processor.h index 94177a9abd..0f2316f6c1 100644 --- a/src/processor.h +++ b/src/processor.h @@ -78,9 +78,6 @@ class Processor { virtual void initVtable(Thread* t, object c) = 0; - virtual void - initClass(Thread* t, object c) = 0; - virtual void visitObjects(Thread* t, Heap::Visitor* v) = 0; @@ -117,23 +114,38 @@ class Processor { getStackTrace(Thread* t, Thread* target) = 0; virtual void - compileThunks(Thread* t, BootImage* image, uint8_t* code, unsigned* size, - unsigned capacity) = 0; + initialize(BootImage* image, uint8_t* code, unsigned capacity) = 0; virtual void - compileMethod(Thread* t, Zone* zone, uint8_t* code, unsigned* offset, - unsigned capacity, object* constants, object* calls, + compileMethod(Thread* t, Zone* zone, object* constants, object* calls, DelayedPromise** addresses, object method) = 0; virtual void - visitRoots(BootImage* image, HeapWalker* w) = 0; + visitRoots(HeapWalker* w) = 0; virtual unsigned* - makeCallTable(Thread* t, BootImage* image, HeapWalker* w, uint8_t* code) = 0; + makeCallTable(Thread* t, HeapWalker* w) = 0; virtual void boot(Thread* t, BootImage* image) = 0; + virtual void + callWithCurrentContinuation(Thread* t, object receiver) = 0; + + virtual void + dynamicWind(Thread* t, object before, object thunk, object after) = 0; + + virtual void + feedResultToContinuation(Thread* t, object continuation, object result) = 0; + + virtual void + feedExceptionToContinuation(Thread* t, object continuation, + object exception) = 0; + + virtual void + walkContinuationBody(Thread* t, Heap::Walker* w, object o, unsigned start) + = 0; + object invoke(Thread* t, object method, object this_, ...) { diff --git a/src/thunks.cpp b/src/thunks.cpp index cdb37c4cc6..bb1721ffe1 100644 --- a/src/thunks.cpp +++ b/src/thunks.cpp @@ -38,7 +38,7 @@ THUNK(makeMultidimensionalArray) THUNK(throw_) THUNK(checkCast) THUNK(instanceOf64) -THUNK(makeNewWeakReference64) +THUNK(makeNewGeneral64) THUNK(makeNew64) THUNK(set) THUNK(gcIfNecessary) diff --git a/src/type-generator.cpp b/src/type-generator.cpp index 2ede6fd9c5..0dce714c46 100644 --- a/src/type-generator.cpp +++ b/src/type-generator.cpp @@ -1323,12 +1323,6 @@ parseJavaClass(Object* type, Stream* s, Object* declarations) } } - if (equal(typeJavaName(type), "java/lang/Class")) { - // add inline vtable - addMember(type, Array::make - (type, 0, "void*", "vtable", sizeOf("void*", 0))); - } - if (typeSuper(type)) { for (Object* p = typeMethods(typeSuper(type)); p; p = cdr(p)) { addMethod(type, car(p)); @@ -1372,23 +1366,25 @@ parseType(Object::ObjectType type, Object* p, Object* declarations, Type* t = Type::make(type, name, javaName); - if (javaName and *javaName != '[') { - assert(cdr(p) == 0); + bool isJavaType = javaName and *javaName != '['; + if (isJavaType) { const char* file = append(javaClassDirectory, "/", javaName, ".class"); Stream s(fopen(file, "rb"), true); parseJavaClass(t, &s, declarations); - } else { - for (p = cdr(p); p; p = cdr(p)) { - if (type == Object::Type) { - parseSubdeclaration(t, car(p), declarations); - } else { - Object* member = parseMember(t, car(p), declarations); - assert(member->type == Object::Scalar); - addMember(t, member); - } - } + } + for (p = cdr(p); p; p = cdr(p)) { + if (type == Object::Type) { + parseSubdeclaration(t, car(p), declarations); + } else { + Object* member = parseMember(t, car(p), declarations); + assert(member->type == Object::Scalar); + addMember(t, member); + } + } + + if (not isJavaType) { if (type == Object::Type and typeSuper(t)) { for (Object* p = typeMethods(typeSuper(t)); p; p = cdr(p)) { addMethod(t, car(p)); diff --git a/src/types.def b/src/types.def index 1fadf50e39..b099fc3397 100644 --- a/src/types.def +++ b/src/types.def @@ -1,13 +1,14 @@ (type jobject java/lang/Object) -(type class java/lang/Class) +(type class java/lang/Class + (array void* vtable)) (type singleton (array uintptr_t body)) (type classLoader java/lang/ClassLoader) -(type systemClassLoader java/lang/SystemClassLoader) +(type systemClassLoader avian/SystemClassLoader) (type accessibleObject java/lang/reflect/AccessibleObject) @@ -94,12 +95,35 @@ (type callNode (intptr_t address) (object target) - (uintptr_t virtualCall) + (uintptr_t flags) (object next)) +(type wordArray + (array uintptr_t body)) + (type array (noassert array object body)) +(type continuationContext + (object next) + (object before) + (object after) + (object continuation) + (object method)) + +(type continuation avian/Continuations$Continuation + (object next) + (object context) + (object method) + (void* address) + (uintptr_t returnAddressOffset) + (uintptr_t framePointerOffset) + (array uintptr_t body)) + +(type unwindResult avian/Continuations$UnwindResult) + +(type callbackReceiver avian/CallbackReceiver) + (type string java/lang/String) (type thread java/lang/Thread) @@ -120,8 +144,9 @@ (type illegalMonitorStateException java/lang/IllegalMonitorStateException) -(type arrayIndexOutOfBoundsException - java/lang/ArrayIndexOutOfBoundsException) +(type indexOutOfBoundsException java/lang/IndexOutOfBoundsException) + +(type arrayIndexOutOfBoundsException java/lang/ArrayIndexOutOfBoundsException) (type arrayStoreException java/lang/ArrayStoreException) @@ -137,6 +162,8 @@ (type error java/lang/Error) +(type virtualMachineError java/lang/VirtualMachineError) + (type stackOverflowError java/lang/StackOverflowError) (type linkageError java/lang/LinkageError) @@ -147,10 +174,15 @@ (type noSuchMethodError java/lang/NoSuchMethodError) +(type noClassDefFoundError java/lang/NoClassDefFoundError) + (type unsatisfiedLinkError java/lang/UnsatisfiedLinkError) (type exceptionInInitializerError java/lang/ExceptionInInitializerError) +(type incompatibleContinuationException + avian/IncompatibleContinuationException) + (type number java/lang/Number) (type byte java/lang/Byte) diff --git a/src/vector.h b/src/vector.h index 78a6059eb0..744ebdff45 100644 --- a/src/vector.h +++ b/src/vector.h @@ -110,6 +110,11 @@ class Vector { append(&v, BytesPerWord); } + void set2(unsigned offset, uint16_t v) { + assert(s, offset <= position - 2); + memcpy(data + offset, &v, 2); + } + unsigned get(unsigned offset) { uint8_t v; get(offset, &v, 1); return v; diff --git a/src/windows.cpp b/src/windows.cpp index 5485a9104b..52e353e82c 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -55,7 +55,7 @@ run(void* r) return 0; } -const bool Verbose = true; +const bool Verbose = false; const unsigned Waiting = 1 << 0; const unsigned Notified = 1 << 1; @@ -523,8 +523,6 @@ class MySystem: public System { } virtual void* tryAllocateExecutable(unsigned sizeInBytes) { - assert(this, sizeInBytes % LikelyPageSizeInBytes == 0); - return VirtualAlloc (0, sizeInBytes, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); } @@ -788,7 +786,8 @@ struct MINIDUMP_USER_STREAM_INFORMATION; struct MINIDUMP_CALLBACK_INFORMATION; enum MINIDUMP_TYPE { - MiniDumpNormal = 0 + MiniDumpNormal = 0, + MiniDumpWithFullMemory = 2 }; typedef BOOL (*MiniDumpWriteDumpType) @@ -828,7 +827,7 @@ dump(LPEXCEPTION_POINTERS e, const char* directory) (GetCurrentProcess(), GetCurrentProcessId(), file, - MiniDumpNormal, + MiniDumpWithFullMemory, &exception, 0, 0); diff --git a/src/x86.S b/src/x86.S index 01d53894fa..5b11db56d6 100644 --- a/src/x86.S +++ b/src/x86.S @@ -12,19 +12,20 @@ #include "types.h" #define LOCAL(x) .L##x + +#if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ +# define GLOBAL(x) _##x +#else +# define GLOBAL(x) x +#endif .text #ifdef __x86_64__ -#ifdef __WINDOWS__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _detectFeature -_detectFeature: -# else -.globl detectFeature -detectFeature: -# endif +#ifdef __MINGW32__ +.globl GLOBAL(detectFeature) +GLOBAL(detectFeature): pushq %rbp movq %rsp, %rbp pushq %rdx @@ -55,13 +56,8 @@ LOCAL(SSEEND): popq %rbp ret -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmNativeCall -_vmNativeCall: -# else -.globl vmNativeCall -vmNativeCall: -# endif +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): pushq %rbp //save nonvolatile registers pushq %r12 @@ -169,21 +165,18 @@ LOCAL(exit): popq %rbp ret -.globl _vmJump -_vmJump: +.globl GLOBAL(vmJump) +GLOBAL(vmJump): movq %rdx,%rbp + movq 8(%rsp),%rax + movq 16(%rsp),%rdx movq %r8,%rsp movq %r9,%rbx jmp *%rcx -#elif defined __LINUX__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _detectFeature -_detectFeature: -# else -.globl detectFeature -detectFeature: -# endif +#else // not __MINGW32__ +.globl GLOBAL(detectFeature) +GLOBAL(detectFeature): pushq %rbp movq %rsp, %rbp pushq %rdx @@ -208,13 +201,8 @@ LOCAL(SSEEND): popq %rbp ret -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmNativeCall -_vmNativeCall: -# else -.globl vmNativeCall -vmNativeCall: -# endif +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): pushq %rbp movq %rsp,%rbp @@ -310,22 +298,21 @@ LOCAL(exit): popq %rbp ret -.globl vmJump -vmJump: +.globl GLOBAL(vmJump) +GLOBAL(vmJump): movq %rsi,%rbp movq %rdx,%rsp movq %rcx,%rbx + movq %r8,%rax + movq %r9,%rdx jmp *%rdi - -#endif //def __WINDOWS__ + +#endif // not __MINGW32__ + #elif defined __i386__ -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _detectFeature -_detectFeature: -# else -.globl detectFeature -detectFeature: -# endif + +.globl GLOBAL(detectFeature) +GLOBAL(detectFeature): pushl %ebp movl %esp, %ebp pushl %edx @@ -355,14 +342,9 @@ LOCAL(SSEEND): movl %ebp,%esp popl %ebp ret - -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmNativeCall -_vmNativeCall: -# else -.globl vmNativeCall -vmNativeCall: -# endif + +.globl GLOBAL(vmNativeCall) +GLOBAL(vmNativeCall): pushl %ebp movl %esp,%ebp @@ -433,16 +415,14 @@ LOCAL(exit): popl %ebp ret -# if defined __APPLE__ || defined __MINGW32__ || defined __CYGWIN32__ -.globl _vmJump -_vmJump: -# else -.globl vmJump -vmJump: -# endif - movl 4(%esp),%eax +.globl GLOBAL(vmJump) +GLOBAL(vmJump): + movl 4(%esp),%esi movl 8(%esp),%ebp movl 16(%esp),%ebx + movl 20(%esp),%eax + movl 24(%esp),%edx movl 12(%esp),%esp - jmp *%eax + jmp *%esi + #endif //def __x86_64__ diff --git a/src/x86.cpp b/src/x86.cpp index c57e3b42fe..a9cc7596bb 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -69,6 +69,9 @@ const unsigned FloatRegisterMask = BytesPerWord == 4 ? 0x00ff0000 : 0xffff0000; const unsigned FrameHeaderSize = 2; +const unsigned StackAlignmentInBytes = 16; +const unsigned StackAlignmentInWords = StackAlignmentInBytes / BytesPerWord; + inline bool isInt8(intptr_t v) { @@ -532,7 +535,7 @@ inline void modrmSib(Context* c, int width, int a, int scale, int index, int bas } } -inline void modrmSibImm(Context* c, int a, int scale, int index, int base, long long offset) { +inline void modrmSibImm(Context* c, int a, int scale, int index, int base, int offset) { if(offset == 0 && regCode(base) != rbp) { modrmSib(c, 0x00, a, scale, index, base); } else if(isInt8(offset)) { @@ -730,13 +733,45 @@ jumpIfLessOrEqualC(Context* c, unsigned size UNUSED, Assembler::Constant* a) } void -jumpIfUnorderedC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +jumpIfFloatUnorderedC(Context* c, unsigned size UNUSED, Assembler::Constant* a) { assert(c, size == BytesPerWord); conditional(c, 0x8a, a); } +void +jumpIfFloatGreaterC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + conditional(c, 0x87, a); +} + +void +jumpIfFloatGreaterOrEqualC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + conditional(c, 0x83, a); +} + +void +jumpIfFloatLessC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + conditional(c, 0x82, a); +} + +void +jumpIfFloatLessOrEqualC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + conditional(c, 0x86, a); +} + void longJumpC(Context* c, unsigned size, Assembler::Constant* a) { @@ -779,7 +814,14 @@ alignedCallC(Context* c, unsigned size, Assembler::Constant* a) } void -pushR(Context* c, UNUSED unsigned size, Assembler::Register* a) +alignedJumpC(Context* c, unsigned size, Assembler::Constant* a) +{ + new (c->zone->allocate(sizeof(AlignmentPadding))) AlignmentPadding(c); + jumpC(c, size, a); +} + +void +pushR(Context* c, unsigned size, Assembler::Register* a) { if (BytesPerWord == 4 and size == 8) { Assembler::Register ah(a->high); @@ -813,6 +855,22 @@ popR(Context* c, unsigned size, Assembler::Register* a) } } +void +popM(Context* c, unsigned size, Assembler::Memory* a) +{ + if (BytesPerWord == 4 and size == 8) { + Assembler::Memory ah(a->base, a->offset + 4, a->index, a->scale); + + popM(c, 4, a); + popM(c, 4, &ah); + } else { + assert(c, BytesPerWord == 4 or size == 8); + + opcode(c, 0x8f); + modrmSibImm(c, 0, a->scale, a->index, a->base, a->offset); + } +} + void addCarryCR(Context* c, unsigned size, Assembler::Constant* a, Assembler::Register* b); @@ -2322,13 +2380,22 @@ populateTables(ArchitectureContext* c) uo[index(Jump, C)] = CAST1(jumpC); uo[index(Jump, M)] = CAST1(jumpM); + uo[index(AlignedJump, C)] = CAST1(alignedJumpC); + uo[index(JumpIfEqual, C)] = CAST1(jumpIfEqualC); uo[index(JumpIfNotEqual, C)] = CAST1(jumpIfNotEqualC); uo[index(JumpIfGreater, C)] = CAST1(jumpIfGreaterC); uo[index(JumpIfGreaterOrEqual, C)] = CAST1(jumpIfGreaterOrEqualC); uo[index(JumpIfLess, C)] = CAST1(jumpIfLessC); uo[index(JumpIfLessOrEqual, C)] = CAST1(jumpIfLessOrEqualC); - uo[index(JumpIfUnordered, C)] = CAST1(jumpIfUnorderedC); + + uo[index(JumpIfFloatUnordered, C)] = CAST1(jumpIfFloatUnorderedC); + uo[index(JumpIfFloatEqual, C)] = CAST1(jumpIfEqualC); + uo[index(JumpIfFloatNotEqual, C)] = CAST1(jumpIfNotEqualC); + uo[index(JumpIfFloatGreater, C)] = CAST1(jumpIfFloatGreaterC); + uo[index(JumpIfFloatGreaterOrEqual, C)] = CAST1(jumpIfFloatGreaterOrEqualC); + uo[index(JumpIfFloatLess, C)] = CAST1(jumpIfFloatLessC); + uo[index(JumpIfFloatLessOrEqual, C)] = CAST1(jumpIfFloatLessOrEqualC); uo[index(LongJump, C)] = CAST1(longJumpC); @@ -2447,6 +2514,11 @@ class MyArchitecture: public Assembler::Architecture { virtual uint64_t floatRegisters() { return supportsSSE() ? FloatRegisterMask : 0; } + + virtual uint64_t allRegisters() { + return generalRegisters() | floatRegisters(); + } + virtual int stack() { return rsp; @@ -2464,6 +2536,14 @@ class MyArchitecture: public Assembler::Architecture { return (BytesPerWord == 4 ? rdx : NoRegister); } + virtual int virtualCallTarget() { + return rax; + } + + virtual int virtualCallIndex() { + return rdx; + } + virtual bool bigEndian() { return false; } @@ -2480,14 +2560,24 @@ class MyArchitecture: public Assembler::Architecture { } } + virtual unsigned frameFootprint(unsigned footprint) { +#ifdef __MINGW32__ + return max(footprint, StackAlignmentInWords); +#else + return max(footprint > argumentRegisterCount() ? + footprint - argumentRegisterCount() : 0, + StackAlignmentInWords); +#endif + } + virtual unsigned argumentFootprint(unsigned footprint) { - return max(footprint, footprint - argumentRegisterCount()); + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); } virtual unsigned argumentRegisterCount() { -#ifdef __WINDOWS__ +#ifdef __MINGW32__ if (BytesPerWord == 8) return 4; else -#elif defined __LINUX__ +#else if (BytesPerWord == 8) return 6; else #endif return 0; @@ -2496,7 +2586,7 @@ class MyArchitecture: public Assembler::Architecture { virtual int argumentRegister(unsigned index) { assert(&c, BytesPerWord == 8); switch (index) { -#ifdef __WINDOWS__ +#ifdef __MINGW32__ case 0: return rcx; case 1: @@ -2505,7 +2595,7 @@ class MyArchitecture: public Assembler::Architecture { return r8; case 3: return r9; -#elif defined __LINUX__ +#else case 0: return rdi; case 1: @@ -2524,9 +2614,20 @@ class MyArchitecture: public Assembler::Architecture { } } - virtual void updateCall(UnaryOperation op UNUSED, - bool assertAlignment UNUSED, void* returnAddress, - void* newTarget) + virtual unsigned stackAlignmentInWords() { + return StackAlignmentInWords; + } + + virtual bool matchCall(void* returnAddress, void* target) { + uint8_t* instruction = static_cast(returnAddress) - 5; + int32_t actualOffset; memcpy(&actualOffset, instruction + 1, 4); + void* actualTarget = static_cast(returnAddress) + actualOffset; + + return *instruction == 0xE8 and actualTarget == target; + } + + virtual void updateCall(UnaryOperation op, bool assertAlignment UNUSED, + void* returnAddress, void* newTarget) { if (BytesPerWord == 4 or op == Call or op == Jump) { uint8_t* instruction = static_cast(returnAddress) - 5; @@ -2566,8 +2667,7 @@ class MyArchitecture: public Assembler::Architecture { } virtual unsigned alignFrameSize(unsigned sizeInWords) { - const unsigned alignment = 16 / BytesPerWord; - return (ceiling(sizeInWords + FrameHeaderSize, alignment) * alignment) + return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) - FrameHeaderSize; } @@ -2590,6 +2690,37 @@ class MyArchitecture: public Assembler::Architecture { virtual bool supportsFloatCompare(unsigned size) { return supportsSSE() and size <= BytesPerWord; } + + virtual bool alwaysCondensed(BinaryOperation op) + { + switch(op) { + case FloatCompare: + case Compare: + case Float2Float: + case Float2Int: + case Int2Float: + return false; + case Negate: + case Abs: + case FloatAbs: + case FloatNegate: + case FloatSqrt: + default: + return true; + } + } + + virtual bool alwaysCondensed(TernaryOperation) { + return true; + } + + virtual int returnAddressOffset() { + return 0; + } + + virtual int framePointerOffset() { + return -1; + } virtual void nextFrame(void** stack, void** base) { assert(&c, *static_cast(*base) != *base); @@ -2979,6 +3110,14 @@ class MyAssembler: public Assembler { BytesPerWord, RegisterOperand, &stack); } + virtual void adjustFrame(unsigned footprint) { + Register stack(rsp); + Constant footprintConstant(resolved(&c, footprint * BytesPerWord)); + apply(Subtract, BytesPerWord, ConstantOperand, &footprintConstant, + BytesPerWord, RegisterOperand, &stack, + BytesPerWord, RegisterOperand, &stack); + } + virtual void popFrame() { Register base(rbp); Register stack(rsp); @@ -2988,6 +3127,91 @@ class MyAssembler: public Assembler { popR(&c, BytesPerWord, &base); } + virtual void popFrameForTailCall(unsigned footprint, + int offset, + int returnAddressSurrogate, + int framePointerSurrogate) + { + if (TailCalls) { + if (offset) { + Register tmp(c.client->acquireTemporary()); + + Memory returnAddressSrc(rsp, (footprint + 1) * BytesPerWord); + moveMR(&c, BytesPerWord, &returnAddressSrc, BytesPerWord, &tmp); + + Memory returnAddressDst(rsp, (footprint - offset + 1) * BytesPerWord); + moveRM(&c, BytesPerWord, &tmp, BytesPerWord, &returnAddressDst); + + c.client->releaseTemporary(tmp.low); + + Memory baseSrc(rsp, footprint * BytesPerWord); + Register base(rbp); + moveMR(&c, BytesPerWord, &baseSrc, BytesPerWord, &base); + + Register stack(rsp); + Constant footprintConstant + (resolved(&c, (footprint - offset + 1) * BytesPerWord)); + addCR(&c, BytesPerWord, &footprintConstant, BytesPerWord, &stack); + + if (returnAddressSurrogate != NoRegister) { + assert(&c, offset > 0); + + Register ras(returnAddressSurrogate); + Memory dst(rsp, offset * BytesPerWord); + moveRM(&c, BytesPerWord, &ras, BytesPerWord, &dst); + } + + if (framePointerSurrogate != NoRegister) { + assert(&c, offset > 0); + + Register fps(framePointerSurrogate); + Memory dst(rsp, (offset - 1) * BytesPerWord); + moveRM(&c, BytesPerWord, &fps, BytesPerWord, &dst); + } + } else { + popFrame(); + } + } else { + abort(&c); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned argumentFootprint) { + popFrame(); + + assert(&c, argumentFootprint >= StackAlignmentInWords); + assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); + + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + Register returnAddress(rcx); + popR(&c, BytesPerWord, &returnAddress); + + Register stack(rsp); + Constant adjustment + (resolved(&c, (argumentFootprint - StackAlignmentInWords) + * BytesPerWord)); + addCR(&c, BytesPerWord, &adjustment, BytesPerWord, &stack); + + jumpR(&c, BytesPerWord, &returnAddress); + } else { + return_(&c); + } + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned stackOffsetFromThread) + { + popFrame(); + + Register returnAddress(rcx); + popR(&c, BytesPerWord, &returnAddress); + + Register stack(rsp); + Memory stackSrc(rbx, stackOffsetFromThread); + moveMR(&c, BytesPerWord, &stackSrc, BytesPerWord, &stack); + + jumpR(&c, BytesPerWord, &returnAddress); + } + virtual void apply(Operation op) { arch_->c.operations[op](&c); } diff --git a/src/x86.h b/src/x86.h index 6a6f0e0935..a7f8e4dd8f 100644 --- a/src/x86.h +++ b/src/x86.h @@ -58,25 +58,24 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t*, # define THREAD_REGISTER(context) (context->uc_mcontext.gregs[REG_RBX]) extern "C" uint64_t -#ifdef __WINDOWS__ +# ifdef __MINGW32__ vmNativeCall(void* function, void* stack, unsigned stackSize, unsigned returnType); -#elif defined __LINUX__ - +# else vmNativeCall(void* function, void* stack, unsigned stackSize, void* gprTable, void* sseTable, unsigned returnType); -#endif //def __WINDOWS__ +# endif namespace vm { -#ifdef __WINDOWS__ +# ifdef __MINGW32__ inline uint64_t dynamicCall(void* function, uint64_t* arguments, UNUSED uint8_t* argumentTypes, unsigned argumentCount, unsigned, unsigned returnType) { return vmNativeCall(function, arguments, argumentCount, returnType); } -#elif defined __LINUX__ +# else inline uint64_t dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, unsigned argumentCount, unsigned, unsigned returnType) @@ -117,7 +116,7 @@ dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes, (gprIndex ? gprTable : 0), (sseIndex ? sseTable : 0), returnType); } -#endif //def __WINDOWS__ +#endif } // namespace vm diff --git a/test/Finalizers.java b/test/Finalizers.java new file mode 100644 index 0000000000..b7844988bf --- /dev/null +++ b/test/Finalizers.java @@ -0,0 +1,34 @@ +public class Finalizers { + private static boolean finalized = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + protected void finalize() { + finalized = true; + } + + public static void main(String[] args) { + new Finalizers(); + + expect(! finalized); + + System.gc(); + + expect(finalized); + + new Finalizers2(); + + finalized = false; + + expect(! finalized); + + System.gc(); + + expect(finalized); + } + + private static class Finalizers2 extends Finalizers { } + +} diff --git a/test/List.java b/test/List.java index ee3ffc0204..797aefbb7f 100644 --- a/test/List.java +++ b/test/List.java @@ -1,4 +1,6 @@ import java.util.ArrayList; +import java.util.LinkedList; +import java.util.ListIterator; public class List { private static void expect(boolean v) { @@ -21,6 +23,114 @@ public class List { expect(s1.equals(s2)); } + private static void testIterators(java.util.List l) { + l.add(1); + l.add(2); + l.add(3); + + ListIterator it = l.listIterator(); + expect(it.next().equals(Integer.valueOf(1))); + expect(it.next().equals(Integer.valueOf(2))); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(1); + expect(it.next().equals(Integer.valueOf(2))); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(2); + expect(it.next().equals(Integer.valueOf(3))); + expect(! it.hasNext()); + + it = l.listIterator(3); + expect(it.previous().equals(Integer.valueOf(3))); + expect(it.previous().equals(Integer.valueOf(2))); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + + it = l.listIterator(2); + expect(it.previous().equals(Integer.valueOf(2))); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + + it = l.listIterator(1); + expect(it.previous().equals(Integer.valueOf(1))); + expect(! it.hasPrevious()); + } + + private static void testIterators2(java.util.List l) { + l.add(1); + l.add(2); + l.add(3); + + ListIterator it = l.listIterator(); + expect(it.next().equals(Integer.valueOf(1))); + it.remove(); + expect(it.next().equals(Integer.valueOf(2))); + it.remove(); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.isEmpty()); + + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(1); + expect(it.next().equals(Integer.valueOf(2))); + it.remove(); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.size() == 1); + + l.add(2); + l.add(3); + + it = l.listIterator(2); + expect(it.next().equals(Integer.valueOf(3))); + it.remove(); + expect(! it.hasNext()); + expect(l.size() == 2); + + l.add(3); + + it = l.listIterator(3); + expect(it.previous().equals(Integer.valueOf(3))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(2))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.isEmpty()); + + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(2); + expect(it.previous().equals(Integer.valueOf(2))); + it.remove(); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.size() == 1); + + l.clear(); + l.add(1); + l.add(2); + l.add(3); + + it = l.listIterator(1); + expect(it.previous().equals(Integer.valueOf(1))); + it.remove(); + expect(! it.hasPrevious()); + expect(l.size() == 2); + } + public static void main(String args[]) { ArrayList l = new ArrayList(); l.add(1); l.add(2); l.add(3); l.add(4); l.add(5); @@ -39,5 +149,11 @@ public class List { for (int i=0; i < z.length; i++) { System.out.println(z[i]); } + + testIterators(new ArrayList()); + testIterators(new LinkedList()); + + testIterators2(new ArrayList()); + testIterators2(new LinkedList()); } } diff --git a/test/Subroutine.java b/test/Subroutine.java index 3ad78b00b8..1a631e25d5 100644 --- a/test/Subroutine.java +++ b/test/Subroutine.java @@ -3,13 +3,14 @@ public class Subroutine { if (! v) throw new RuntimeException(); } - // This test is intended to cover the jsr and ret instructions. + // These tests are intended to cover the jsr and ret instructions. // However, recent Sun javac versions avoid generating these // instructions by default, so we must compile this class using // -source 1.2 -target 1.1 -XDjsrlimit=0. // // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4381996 // + private static void test(boolean throw_, boolean predicate) { int x = 42; int y = 99; @@ -32,10 +33,159 @@ public class Subroutine { } } + private static Object test2(int path) { + try { + try { + switch (path) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static Object test3(int path1, int path2, int path3) { + try { + try { + switch (path1) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path2) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + try { + switch (path3) { + case 1: + return new Object(); + + case 2: { + int a = 42; + return Integer.valueOf(a); + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + } + } + return null; + } catch (DummyException e) { + e.printStackTrace(); + return null; + } + } + + private static long test4(int path) { + try { + try { + switch (path) { + case 1: + return 42L; + + case 2: { + int a = 42; + return 52L; + } + + case 3: + throw new DummyException(); + } + } finally { + System.gc(); + } + return 0L; + } catch (DummyException e) { + e.printStackTrace(); + return 0L; + } + } + public static void main(String[] args) { test(false, false); test(false, true); test(true, false); + + String.valueOf(test2(1)); + String.valueOf(test2(2)); + String.valueOf(test2(3)); + + String.valueOf(test3(1, 1, 1)); + String.valueOf(test3(2, 1, 1)); + String.valueOf(test3(3, 1, 1)); + + String.valueOf(test3(1, 2, 1)); + String.valueOf(test3(2, 2, 1)); + String.valueOf(test3(3, 2, 1)); + + String.valueOf(test3(1, 3, 1)); + String.valueOf(test3(2, 3, 1)); + String.valueOf(test3(3, 3, 1)); + + String.valueOf(test3(1, 1, 2)); + String.valueOf(test3(2, 1, 2)); + String.valueOf(test3(3, 1, 2)); + + String.valueOf(test3(1, 2, 2)); + String.valueOf(test3(2, 2, 2)); + String.valueOf(test3(3, 2, 2)); + + String.valueOf(test3(1, 3, 2)); + String.valueOf(test3(2, 3, 2)); + String.valueOf(test3(3, 3, 2)); + + String.valueOf(test3(1, 1, 3)); + String.valueOf(test3(2, 1, 3)); + String.valueOf(test3(3, 1, 3)); + + String.valueOf(test3(1, 2, 3)); + String.valueOf(test3(2, 2, 3)); + String.valueOf(test3(3, 2, 3)); + + String.valueOf(test3(1, 3, 3)); + String.valueOf(test3(2, 3, 3)); + String.valueOf(test3(3, 3, 3)); + + String.valueOf(test4(1)); + String.valueOf(test4(2)); + String.valueOf(test4(3)); } private static class DummyException extends RuntimeException { } diff --git a/test/Tree.java b/test/Tree.java index 5d4ad83d0c..ded42a27ea 100644 --- a/test/Tree.java +++ b/test/Tree.java @@ -1,5 +1,8 @@ import java.util.Comparator; import java.util.TreeSet; +import java.util.TreeMap; +import java.util.Map; +import java.util.Iterator; public class Tree { private static void expect(boolean v) { @@ -17,6 +20,21 @@ public class Tree { return sb.toString(); } + private static String printMap(TreeMap map) { + StringBuilder sb = new StringBuilder(); + + for (Iterator it = map.entrySet().iterator(); it.hasNext();) { + Map.Entry e = it.next(); + sb.append(e.getKey()); + sb.append("="); + sb.append(e.getValue()); + if (it.hasNext()) { + sb.append(", "); + } + } + return sb.toString(); + } + private static void isEqual(String s1, String s2) { System.out.println(s1); expect(s1.equals(s2)); @@ -53,5 +71,21 @@ public class Tree { expect(t2.size() == 6); t2.add("kappa"); isEqual(printList(t2), "999, five, four, kappa, one, three, two"); + + TreeMap map = new TreeMap + (new Comparator() { + public int compare(String s1, String s2) { + return s1.compareTo(s2); + } + }); + + map.put("q", "Q"); + map.put("a", "A"); + map.put("b", "B"); + map.put("z", "Z"); + map.put("c", "C"); + map.put("y", "Y"); + + isEqual(printMap(map), "a=A, b=B, c=C, q=Q, y=Y, z=Z"); } } diff --git a/test/Zip.java b/test/Zip.java index b18d5f1a0b..2aea1760f1 100644 --- a/test/Zip.java +++ b/test/Zip.java @@ -9,7 +9,9 @@ public class Zip { ZipFile file = new ZipFile("build/classpath.jar"); byte[] buffer = new byte[4096]; - for (Enumeration e = file.entries(); e.hasMoreElements();) { + for (Enumeration e = file.entries(); + e.hasMoreElements();) + { ZipEntry entry = e.nextElement(); InputStream in = file.getInputStream(entry); try { diff --git a/test/extra/Continuations.java b/test/extra/Continuations.java new file mode 100644 index 0000000000..a439d8a58d --- /dev/null +++ b/test/extra/Continuations.java @@ -0,0 +1,54 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; + +import avian.CallbackReceiver; +import avian.Callback; + +public class Continuations { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + public static void main(String[] args) throws Exception { + expect(callWithCurrentContinuation(new CallbackReceiver() { + public Integer receive(Callback continuation) { + continuation.handleResult(42); + throw new AssertionError(); + } + }) == 42); + + expect(callWithCurrentContinuation(new CallbackReceiver() { + public Integer receive(Callback continuation) { + return 43; + } + }) == 43); + + try { + callWithCurrentContinuation(new CallbackReceiver() { + public Integer receive(Callback continuation) { + continuation.handleException(new MyException()); + throw new AssertionError(); + } + }); + throw new AssertionError(); + } catch (MyException e) { + e.printStackTrace(); + } + + try { + callWithCurrentContinuation(new CallbackReceiver() { + public Integer receive(Callback continuation) + throws MyException + { + throw new MyException(); + } + }); + throw new AssertionError(); + } catch (MyException e) { + e.printStackTrace(); + } + } + + private static class MyException extends Exception { } +} diff --git a/test/extra/Coroutines.java b/test/extra/Coroutines.java new file mode 100644 index 0000000000..7d4ee9c5a5 --- /dev/null +++ b/test/extra/Coroutines.java @@ -0,0 +1,93 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; + +import avian.CallbackReceiver; +import avian.Callback; + +public class Coroutines { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void produce(Consumer consumer) throws Exception { + System.out.println("produce \"a\""); + consumer.consume('a'); + + System.out.println("produce \"b\""); + consumer.consume('b'); + + System.out.println("produce \"c\""); + consumer.consume('c'); + } + + private static void consume(Producer producer) throws Exception { + char v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'a'); + + v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'b'); + + v = producer.produce(); + System.out.println("consume \"" + v + "\""); + expect(v == 'c'); + } + + public static void main(String[] args) throws Exception { + final CoroutineState state = new CoroutineState(); + + final Consumer consumer = new Consumer() { + public void consume(final Character c) throws Exception { + callWithCurrentContinuation(new CallbackReceiver() { + public Object receive(Callback continuation) { + state.produceNext = continuation; + + state.consumeNext.handleResult(c); + + throw new AssertionError(); + } + }); + } + }; + + final Producer producer = new Producer() { + final CallbackReceiver receiver + = new CallbackReceiver() { + public Character receive(Callback continuation) + throws Exception + { + state.consumeNext = continuation; + + if (state.produceNext == null) { + Coroutines.produce(consumer); + } else { + state.produceNext.handleResult(null); + } + + throw new AssertionError(); + } + }; + + public Character produce() throws Exception { + return callWithCurrentContinuation(receiver); + } + }; + + consume(producer); + } + + private static class CoroutineState { + public Callback produceNext; + public Callback consumeNext; + } + + private interface Producer { + public T produce() throws Exception; + } + + private interface Consumer { + public void consume(T value) throws Exception; + } +} diff --git a/test/extra/DynamicWind.java b/test/extra/DynamicWind.java new file mode 100644 index 0000000000..aa617ddc9c --- /dev/null +++ b/test/extra/DynamicWind.java @@ -0,0 +1,335 @@ +package extra; + +import static avian.Continuations.callWithCurrentContinuation; +import static avian.Continuations.dynamicWind; + +import avian.CallbackReceiver; +import avian.Callback; + +import java.util.concurrent.Callable; + +public class DynamicWind { + private int before; + private int task; + private int after; + private int continuationCount; + private Callback continuationReference; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private void unwindTest(final Callable unwind) throws Exception { + System.out.println("unwindTest enter"); + + try { + expect(dynamicWind(new Runnable() { + public void run() { + System.out.println("unwindTest before"); + + expect(before == 0); + expect(task == 0); + expect(after == 0); + + before = 1; + } + }, new Callable() { + public Integer call() throws Exception { + System.out.println("unwindTest thunk"); + + expect(before == 1); + expect(task == 0); + expect(after == 0); + + task = 1; + + return unwind.call(); + } + }, + new Runnable() { + public void run() { + System.out.println("unwindTest after"); + + expect(before == 1); + expect(task == 1); + expect(after == 0); + + after = 1; + } + }) == 42); + } catch (MyException e) { + e.printStackTrace(); + } + + System.out.println("unwindTest expect"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + System.out.println("unwindTest exit"); + } + + private void normalUnwind() throws Exception { + unwindTest(new Callable() { + public Integer call() { + return 42; + } + }); + } + + private void exceptionUnwind() throws Exception { + unwindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(); + } + }); + } + + private void continuationUnwindTest(final CallbackReceiver receiver) + throws Exception + { + System.out.println("continuationUnwindTest enter"); + + try { + expect(callWithCurrentContinuation(new CallbackReceiver() { + public Integer receive(final Callback continuation) + throws Exception + { + unwindTest(new Callable() { + public Integer call() throws Exception { + return receiver.receive(continuation); + } + }); + throw new AssertionError(); + } + }) == 42); + } catch (MyException e) { + e.printStackTrace(); + } + + System.out.println("continuationUnwindTest expect"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + System.out.println("continuationUnwindTest exit"); + } + + private void continuationResultUnwind() throws Exception { + continuationUnwindTest(new CallbackReceiver() { + public Integer receive(final Callback continuation) { + continuation.handleResult(42); + throw new AssertionError(); + } + }); + } + + private void continuationExceptionUnwind() throws Exception { + continuationUnwindTest(new CallbackReceiver() { + public Integer receive(final Callback continuation) { + continuation.handleException(new MyException()); + throw new AssertionError(); + } + }); + } + + private void rewindTest(final Callable unwind, Runnable rewind) + throws Exception + { + System.out.println("rewindTest enter"); + + int value; + try { + value = dynamicWind(new Runnable() { + public void run() { + System.out.println("rewindTest before"); + + expect(before == continuationCount); + expect(task == continuationCount); + expect(after == continuationCount); + + ++ before; + } + }, new Callable() { + public Integer call() throws Exception { + System.out.println("rewindTest thunk"); + + expect(before == 1); + expect(task == 0); + expect(after == 0); + + task = 1; + + return callWithCurrentContinuation + (new CallbackReceiver() { + public Integer receive(final Callback continuation) + throws Exception + { + continuationReference = continuation; + return unwind.call(); + } + }); + } + }, new Runnable() { + public void run() { + System.out.println("rewindTest after"); + + expect(before == continuationCount + 1); + expect(task == 1); + expect(after == continuationCount); + + ++ after; + } + }); + } catch (MyException e) { + value = e.value; + } + + System.out.println("rewindTest expect"); + + expect(value == continuationCount); + + if (value == 0) { + System.out.println("rewindTest expect 0"); + + expect(before == 1); + expect(task == 1); + expect(after == 1); + + continuationCount = 1; + rewind.run(); + throw new AssertionError(); + } else { + System.out.println("rewindTest expect 1"); + + expect(value == 1); + expect(before == 2); + expect(task == 1); + expect(after == 2); + } + + System.out.println("rewindTest exit"); + } + + private void continuationResultRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() { + return 0; + } + }, new Runnable() { + public void run() { + continuationReference.handleResult(1); + } + }); + } + + private void continuationExceptionRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(0); + } + }, new Runnable() { + public void run() { + continuationReference.handleException(new MyException(1)); + } + }); + } + + private void continuationResultUnwindAndRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() { + return 0; + } + }, new Runnable() { + public void run() { + try { + new DynamicWind().unwindTest(new Callable() { + public Integer call() { + continuationReference.handleResult(1); + throw new AssertionError(); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void continuationExceptionUnwindAndRewind() throws Exception { + rewindTest(new Callable() { + public Integer call() throws Exception { + throw new MyException(0); + } + }, new Runnable() { + public void run() { + try { + new DynamicWind().unwindTest(new Callable() { + public Integer call() { + continuationReference.handleException(new MyException(1)); + throw new AssertionError(); + } + }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void continuationResultUnwindAndRewindWithShared() throws Exception { + unwindTest(new Callable() { + public Integer call() throws Exception { + new DynamicWind().continuationResultUnwindAndRewind(); + return 42; + } + }); + } + + private void continuationExceptionUnwindAndRewindWithShared() + throws Exception + { + unwindTest(new Callable() { + public Integer call() throws Exception { + new DynamicWind().continuationExceptionUnwindAndRewind(); + return 42; + } + }); + } + + public static void main(String[] args) throws Exception { + new DynamicWind().normalUnwind(); + + new DynamicWind().exceptionUnwind(); + + new DynamicWind().continuationResultUnwind(); + + new DynamicWind().continuationExceptionUnwind(); + + new DynamicWind().continuationResultRewind(); + + new DynamicWind().continuationExceptionRewind(); + + new DynamicWind().continuationResultUnwindAndRewind(); + + new DynamicWind().continuationExceptionUnwindAndRewind(); + + new DynamicWind().continuationResultUnwindAndRewindWithShared(); + + new DynamicWind().continuationExceptionUnwindAndRewindWithShared(); + } + + private static class MyException extends Exception { + public final int value; + + public MyException() { + this(0); + } + + public MyException(int value) { + this.value = value; + } + } +} diff --git a/test/extra/Memory.java b/test/extra/Memory.java index 24b837c716..b2e8cfbfea 100644 --- a/test/extra/Memory.java +++ b/test/extra/Memory.java @@ -1,3 +1,5 @@ +package extra; + import java.util.Collection; import java.util.Comparator; import java.util.HashMap; diff --git a/test/extra/RuntimeExec.java b/test/extra/RuntimeExec.java index 2895c61bf9..23adbb9105 100644 --- a/test/extra/RuntimeExec.java +++ b/test/extra/RuntimeExec.java @@ -1,3 +1,5 @@ +package extra; + import java.lang.Runtime; import java.lang.Process; diff --git a/test/extra/SendFile.java b/test/extra/SendFile.java index ad525a1e05..da77a33a21 100644 --- a/test/extra/SendFile.java +++ b/test/extra/SendFile.java @@ -1,3 +1,5 @@ +package extra; + import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.io.IOException; diff --git a/test/extra/SendServer.java b/test/extra/SendServer.java index e2877d3b22..5f60c962c1 100644 --- a/test/extra/SendServer.java +++ b/test/extra/SendServer.java @@ -1,3 +1,5 @@ +package extra; + import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; diff --git a/vm.pro b/vm.pro index ad77a5eb57..3ba4620c0e 100644 --- a/vm.pro +++ b/vm.pro @@ -10,7 +10,6 @@ -keepclassmembers class java.lang.Class { !static ; } -keepclassmembers class java.lang.ClassLoader { !static ; } --keepclassmembers class java.lang.SystemClassLoader { !static ; } -keepclassmembers class java.lang.String { !static ; } -keepclassmembers class java.lang.Thread { !static ; } -keepclassmembers class java.lang.StackTraceElement { !static ; } @@ -32,6 +31,7 @@ # the VM may throw instances of the following: +-keep public class avian.IncompatibleContinuationException -keep public class java.lang.RuntimeException -keep public class java.lang.IllegalStateException -keep public class java.lang.IllegalArgumentException @@ -54,7 +54,7 @@ # ClassLoader.getSystemClassloader() depends on the existence of this class: --keep class java.lang.SystemClassLoader +-keep class avian.SystemClassLoader # the VM references these classes by name, so protect them from obfuscation: @@ -73,4 +73,22 @@ # Thread.run is called by name in the VM --keepclassmembernames class java.lang.Thread { void run(); } +-keepclassmembers class java.lang.Thread { + private static void run(java.lang.Thread); + } + +# when continuations are enabled, the VM may call these methods by name: + +-keepclassmembers class avian.Continuations { + *** wind(...); + *** rewind(...); + } + +-keepclassmembernames class avian.CallbackReceiver { + *** receive(...); + } + +# the above methods include these classes in their signatures: + +-keepnames public class avian.Callback +-keepnames public class java.util.concurrent.Callable