diff --git a/.gitignore b/.gitignore index 201518bb12..588446f90d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build bin /lib /distrib +*.pdb diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..18dcccd6af --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: cpp +script: ./test/ci.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000000..1aba5f6f69 --- /dev/null +++ b/README.md @@ -0,0 +1,804 @@ +Avian - A lightweight Java Virtual Machine (JVM) +================================================ + +[![Build Status](https://travis-ci.org/ReadyTalk/avian.png?branch=master)](https://travis-ci.org/ReadyTalk/avian) + +Quick Start +----------- + +#### on Linux: + $ export JAVA_HOME=/usr/local/java # or wherever you have the JDK installed + $ make + $ build/linux-i386/avian -cp build/linux-i386/test Hello + +#### on Mac OS X: + $ export JAVA_HOME=/Library/Java/Home + $ make + $ build/darwin-i386/avian -cp build/darwin-i386/test Hello + +#### on Windows (MSYS): + $ git clone git@github.com:ReadyTalk/win32.git ../win32 + $ export JAVA_HOME="C:/Program Files/Java/jdk1.6.0_07" + $ make + $ build/windows-i386/avian -cp build/windows-i386/test Hello + +#### on Windows (Cygwin): + $ git clone git@github.com:ReadyTalk/win32.git ../win32 + $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.6.0_07" + $ make + $ build/windows-i386/avian -cp build/windows-i386/test Hello + +#### on FreeBSD: + $ export JAVA_HOME=/usr/local/openjdk7 # or wherever you have the JDK installed + $ gmake + $ build/freebsd-x86_64/avian -cp build/freebsd-x86_64/test Hello + +Adjust JAVA_HOME according to your system, but be sure to use forward +slashes in the path. + + +Introduction +------------ + +Avian is a lightweight virtual machine and class library designed to +provide a useful subset of Java's features, suitable for building +self-contained applications. More information is available at the +project [web site](http://oss.readytalk.com/avian). + +If you have any trouble building, running, or embedding Avian, please +post a message to our [discussion group](http://groups.google.com/group/avian). + +That's also the place for any other questions, comments, or +suggestions you might have. + + +Supported Platforms +------------------- + +Avian can currently target the following platforms: + + * Linux (i386, x86_64, ARM, and 32-bit PowerPC) + * Windows (i386 and x86_64) + * Mac OS X (i386, x86_64 and 32-bit PowerPC) + * Apple iOS (i386 and ARM) + * FreeBSD (i386, x86_64) + + +Building +-------- + +Build requirements include: + + * GNU make 3.80 or later + * GCC 3.4 or later (4.5.1 or later for Windows/x86_64) + or LLVM Clang 3.1 or later (see use-clang option below) + * JDK 1.5 or later + * MinGW 3.4 or later (only if compiling for Windows) + * zlib 1.2.3 or later + +Earlier versions of some of these packages may also work but have not +been tested. + +The build is directed by a single makefile and may be influenced via +certain flags described below, all of which are optional. + + $ make \ + platform={linux,windows,darwin,freebsd} \ + arch={i386,x86_64,powerpc,arm} \ + process={compile,interpret} \ + mode={debug,debug-fast,fast,small} \ + lzma= \ + ios={true,false} \ + bootimage={true,false} \ + heapdump={true,false} \ + tails={true,false} \ + continuations={true,false} \ + use-clang={true,false} \ + openjdk= \ + openjdk-src= \ + android= + + * `platform` - the target platform + * _default:_ output of $(uname -s | tr [:upper:] [:lower:]), +normalized in some cases (e.g. CYGWIN_NT-5.1 -> windows) + + * `arch` - the target architecture + * _default:_ output of $(uname -m), normalized in some cases +(e.g. i686 -> i386) + + * `process` - choice between pure interpreter or JIT compiler + * _default:_ compile + + * `mode` - which set of compilation flags to use to determine +optimization level, debug symbols, and whether to enable +assertions + * _default:_ fast + + * `lzma` - if set, support use of LZMA to compress embedded JARs and +boot images. The value of this option should be a directory +containing a recent LZMA SDK (available [here](http://www.7-zip.org/sdk.html)). Currently, only version 9.20 of +the SDK has been tested, but other versions might work. + * _default:_ not set + + * `ios` - if true, cross-compile for iOS on OS X. Note that +non-jailbroken iOS devices do not allow JIT compilation, so only +process=interpret or bootimage=true builds will run on such +devices. See [here](https://github.com/ReadyTalk/hello-ios) for an +example of an Xcode project for iOS which uses Avian. + * _default:_ false + + * `bootimage` - if true, create a boot image containing the pre-parsed +class library and ahead-of-time compiled methods. This option is +only valid for process=compile builds. Note that you may need to +specify both build-arch=x86_64 and arch=x86_64 on 64-bit systems +where "uname -m" prints "i386". + * _default:_ false + + * `heapdump` - if true, implement avian.Machine.dumpHeap(String), +which, when called, will generate a snapshot of the heap in a +simple, ad-hoc format for memory profiling purposes. See +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 + + * `use-clang` - if true, use LLVM's clang instead of GCC to build. +Note that this does not currently affect cross compiles, only +native builds. + * _default:_ false + + * `openjdk` - if set, use the OpenJDK class library instead of the +default Avian class library. See "Building with the OpenJDK Class +Library" below for details. + * _default:_ not set + + * `openjdk-src` - if this and the openjdk option above are both set, +build an embeddable VM using the OpenJDK class library. The JNI +components of the OpenJDK class library will be built from the +sources found under the specified directory. See "Building with +the OpenJDK Class Library" below for details. + * _default:_ not set + + * `android` - if set, use the Android class library instead of the +default Avian class library. See "Building with the Android Class +Library" below for details. + * _default:_ not set + +These flags determine the name of the directory used for the build. +The name always starts with _${platform}-${arch}_, and each non-default +build option is appended to the name. For example, a debug build with +bootimage enabled on Linux/i386 would be built in +_build/linux-i386-debug-bootimage_. This allows you to build with +several different sets of options independently and even +simultaneously without doing a clean build each time. + +If you are compiling for Windows, you may either cross-compile using +MinGW or build natively on Windows under MSYS or Cygwin. + +#### Installing MSYS: + + __1.__ Download and install the current MinGW and MSYS packages from + mingw.org, selecting the C and C++ compilers when prompted. Use the + post-install script to create the filesystem link to the compiler. + + __2.__ Download GNU Make 3.81 from the MSYS download page + (make-3.81-MSYS-1.0.11-2.tar.bz2) and extract the tar file into + _e.g. c:/msys/1.0_. + +#### Installing Cygwin: + + __1.__ Download and run setup.exe from [cygwin's website](http://www.cygwin.com), installing the base + system and these packages: make, gcc-mingw-g++, + mingw64-i686-gcc-g++, mingw64-x86_64-gcc-g++, and (optionally) git. + +You may also find our win32 repository useful: (run this from the +directory containing the avian directory) + + $ git clone git@github.com:ReadyTalk/win32.git + +This gives you the Windows JNI headers, zlib headers and library, and +a few other useful libraries like OpenSSL, libjpeg, and libpng. +There's also a win64 repository for 64-bit builds: + + $ git clone git@github.com:ReadyTalk/win64.git + + +Building with the Microsoft Visual C++ Compiler +----------------------------------------------- + +You can also build using the MSVC compiler, which makes debugging with +tools like WinDbg and Visual Studio much easier. Note that you will +still need to have GCC installed - MSVC is only used to compile the +C++ portions of the VM, while the assembly code and helper tools are +built using GCC. + +The MSVC build has been tested with Visual Studio Express Edition +versions 8, 9, and 10. Other versions may also work. + +To build with MSVC, install Cygwin as described above and set the +following environment variables: + + $ export PATH="/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/BIN:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/Tools:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v3.5:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/VCPackages:/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.0A/bin:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS:/cygdrive/c/WINDOWS/System32/Wbem" + $ export LIBPATH="C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;" + $ export VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0\VC" + $ export LIB="C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib;" + $ export INCLUDE="C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include;" + +Adjust these definitions as necessary according to your MSVC +installation. + +Finally, build with the msvc flag set to the MSVC tool directory: + + $ make msvc="/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC" + + +Building with the OpenJDK Class Library +--------------------------------------- + +By default, Avian uses its own lightweight class library. However, +that library only contains a relatively small subset of the classes +and methods included in the JRE. If your application requires +features beyond that subset, you may want to tell Avian to use +OpenJDK's class library instead. To do so, specify the directory +where OpenJDK is installed, e.g.: + + $ make openjdk=/usr/lib/jvm/java-7-openjdk + +This will build Avian as a conventional JVM (e.g. libjvm.so) which +loads its boot class library and native libraries (e.g. libjava.so) +from _/usr/lib/jvm/java-7-openjdk/jre_ at runtime. Note that you must +use an absolute path here, or else the result will not work when run +from other directories. In this configuration, OpenJDK needs to +remain installed for Avian to work, and you can run applications like +this: + + $ build/linux-x86_64-openjdk/avian-dynamic -cp /path/to/my/application \ + com.example.MyApplication + +Alternatively, you can enable a stand-alone build using OpenJDK by +specifying the location of the OpenJDK source code, e.g.: + + $ make openjdk=$(pwd)/../jdk7/build/linux-amd64/j2sdk-image \ + openjdk-src=$(pwd)/../jdk7/jdk/src + +You must ensure that the path specified for openjdk-src does not have +any spaces in it; make gets confused when dependency paths include +spaces, and we haven't found away around that except to avoid paths +with spaces entirely. + +The result of such a build is a self-contained binary which does not +depend on external libraries, jars, or other files. In this case, the +specified paths are used only at build time; anything needed at +runtime is embedded in the binary. Thus, the process of running an +application is simplified: + + $ build/linux-x86_64-openjdk-src/avian -cp /path/to/my/application \ + com.example.MyApplication + +Note that the resulting binary will be very large due to the size of +OpenJDK's class library. This can be mitigated using UPX, preferably +an LZMA-enabled version: + + $ upx --lzma --best build/linux-x86_64-openjdk-src/avian + +You can reduce the size futher for embedded builds by using ProGuard +and the supplied openjdk.pro configuration file (see "Embedding with +ProGuard and a Boot Image" below). Note that you'll still need to use +vm.pro in that case -- openjdk.pro just adds additional constraints +specific to the OpenJDK port. Also see app.mk in +_git://oss.readytalk.com/avian-swt-examples.git_ for an example of using +Avian, OpenJDK, ProGuard, and UPX in concert. + +Here are some examples of how to install OpenJDK and build Avian with +it on various OSes: + +#### Debian-based Linux: +_Conventional build:_ + + $ apt-get install openjdk-7-jdk + $ make openjdk=/usr/lib/jvm/java-7-openjdk test + +_Stand-alone build:_ + + $ apt-get install openjdk-7-jdk + $ apt-get source openjdk-7-jdk + $ apt-get build-dep openjdk-7-jdk + $ (cd openjdk-7-7~b147-2.0 && dpkg-buildpackage) + $ make openjdk=/usr/lib/jvm/java-7-openjdk \ + openjdk-src=$(pwd)/openjdk-7-7~b147-2.0/build/openjdk/jdk/src \ + test + +####Mac OS X: +_Prerequisite:_ Build OpenJDK 7 according to [this site](https://wikis.oracle.com/display/OpenJDK/Mac+OS+X+Port). + +_Conventional build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image test + +_Stand-alone build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image \ + openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test + +####Windows (Cygwin): +_Prerequisite:_ Build OpenJDK 7 according to [this site](http://weblogs.java.net/blog/simonis/archive/2011/10/28/yaojowbi-yet-another-openjdk-windows-build-instruction). Alternatively, use https://github.com/alexkasko/openjdk-unofficial-builds. + +_Conventional build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image test + +_Stand-alone build:_ + + $ make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image \ + openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test + +Currently, only OpenJDK 7 is supported. Later versions might work, +but have not yet been tested. + + +Building with the Android Class Library +--------------------------------------- + +As an alternative to both the Avian and OpenJDK class libaries, you +can also build with the Android class library on some platforms +(currently Linux works and OS X mostly works). To build this way, do +the following, starting from the Avian directory: + + cd .. + mkdir -p android/system android/external + cd android + git clone https://android.googlesource.com/platform/bionic + git clone https://android.googlesource.com/platform/system/core \ + system/core + git clone https://android.googlesource.com/platform/external/expat \ + external/expat + git clone https://android.googlesource.com/platform/external/fdlibm \ + external/fdlibm + git clone https://android.googlesource.com/platform/external/icu4c \ + external/icu4c + git clone https://android.googlesource.com/platform/libnativehelper + git clone https://android.googlesource.com/platform/external/openssl \ + external/openssl + git clone https://android.googlesource.com/platform/external/zlib \ + external/zlib + git clone git://git.openssl.org/openssl.git openssl-upstream + git clone https://github.com/dicej/android-libcore64 libcore + (cd external/expat && CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure \ + --enable-static && make) + (cd external/fdlibm && (mv makefile.in Makefile.in || true) \ + && CFLAGS=-fPIC bash configure && make) + (cd external/icu4c && CFLAGS=-fPIC CXXFLAGS=-fPIC ./configure \ + --enable-static && make) + +NB: use 'CC="gcc -fPIC" ./Configure darwin64-x86_64-cc' when building +for x86_64 OS X instead of 'CC="gcc -fPIC" ./config': + + (cd openssl-upstream && git checkout OpenSSL_1_0_1e \ + && (for x in ../external/openssl/patches/*.patch; \ + do patch -p1 < $x; done) \ + && CC="gcc -fPIC" ./config && make) + cd ../avian + make android=$(pwd)/../android test + +Note that we use https://github.com/dicej/android-libcore64 above +instead of the upstream +https://android.googlesource.com/platform/libcore repository, since +the former has patches to provide better support for non-Linux platforms. + +Also note that we use the upstream OpenSSL repository and apply the +Android patches to it. This is because it is not clear how to build +the Android fork of OpenSSL directly without checking out and building +the entire platform. As of this writing, the patches apply cleanly +against OpenSSL 1.0.1e, so that's the tag we check out, but this may +change in the future when the Android fork rebases against a new +OpenSSL version. + + +Installing +---------- + +Installing Avian is as simple as copying the executable to the desired +directory: + + $ cp build/${platform}-${arch}/avian ~/bin/ + + +Embedding +--------- + +The following series of commands illustrates how to produce a +stand-alone executable out of a Java application using Avian. + +Note: if you are building on Cygwin, prepend "x86_64-w64-mingw32-" or +"i686-w64-mingw32-" to the ar, g++, gcc, strip, and dlltool commands +below (e.g. x86_64-w64-mingw32-gcc). + +__1.__ Build Avian, create a new directory, and populate it with the +VM object files and bootstrap classpath jar. + + $ make + $ mkdir hello + $ cd hello + $ ar x ../build/${platform}-${arch}/libavian.a + $ cp ../build/${platform}-${arch}/classpath.jar boot.jar + +__2.__ Build the Java code and add it to the jar. + + $ cat >Hello.java <embedded-jar-main.cpp <("-Xbootclasspath:[bootJar]"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + jclass c = e->FindClass("Hello"); + if (not e->ExceptionCheck()) { + jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); + if (not e->ExceptionCheck()) { + jclass stringClass = e->FindClass("java/lang/String"); + if (not e->ExceptionCheck()) { + jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); + if (not e->ExceptionCheck()) { + for (int i = 1; i < ac; ++i) { + e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); + } + + e->CallStaticVoidMethod(c, m, a); + } + } + } + } + + int exitCode = 0; + if (e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + } + + vm->DestroyJavaVM(); + + return exitCode; + } + EOF + +__on Linux:__ + + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ + -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o + +__on Mac OS X:__ + + $ g++ -I$JAVA_HOME/include -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp \ + -o main.o + +__on Windows:__ + + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 \ + -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o + +__5.__ Link the objects produced above to produce the final +executable, and optionally strip its symbols. + +__on Linux:__ + + $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello + $ strip --strip-all hello + +__on Mac OS X:__ + + $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello -framework CoreFoundation + $ strip -S -x hello + +__on Windows:__ + + $ dlltool -z hello.def *.o + $ dlltool -d hello.def -e hello.exp + $ g++ hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \ + -mwindows -mconsole -o hello.exe + $ strip --strip-all hello.exe + +Embedding with ProGuard and a Boot Image +---------------------------------------- + +The following illustrates how to embed an application as above, except +this time we preprocess the code using ProGuard and build a boot image +from it for quicker startup. The pros and cons of using ProGuard are +as follow: + + * Pros: ProGuard will eliminate unused code, optimize the rest, and + obfuscate it as well for maximum space savings + + * Cons: increased build time, especially for large applications, and + extra effort needed to configure it for applications which rely + heavily on reflection and/or calls to Java from native code + +For boot image builds: + + * Pros: the boot image build pre-parses all the classes and compiles + all the methods, obviating the need for JIT compilation at runtime. + This also makes garbage collection faster, since the pre-parsed + classes are never visited. + + * Cons: the pre-parsed classes and AOT-compiled methods take up more + space in the executable than the equivalent class files. In + practice, this can make the executable 30-50% larger. Also, AOT + compilation does not yet yield significantly faster or smaller code + than JIT compilation. Finally, floating point code may be slower + on 32-bit x86 since the compiler cannot assume SSE2 support will be + available at runtime, and the x87 FPU is not supported except via + out-of-line helper functions. + +Note you can use ProGuard without using a boot image and vice-versa, +as desired. + +The following instructions assume we are building for Linux/i386. +Please refer to the previous example for guidance on other platforms. + +__1.__ Build Avian, create a new directory, and populate it with the +VM object files. + + $ make bootimage=true + $ mkdir hello + $ cd hello + $ ar x ../build/linux-i386-bootimage/libavian.a + +__2.__ Create a stage1 directory and extract the contents of the +class library jar into it. + + $ mkdir stage1 + $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar) + +__3.__ Build the Java code and add it to stage1. + + $ cat >Hello.java <hello.pro <bootimage-main.cpp <("-Davian.bootimage=bootimageBin"); + + options[1].optionString + = const_cast("-Davian.codeimage=codeimageBin"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + jclass c = e->FindClass("Hello"); + if (not e->ExceptionCheck()) { + jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); + if (not e->ExceptionCheck()) { + jclass stringClass = e->FindClass("java/lang/String"); + if (not e->ExceptionCheck()) { + jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); + if (not e->ExceptionCheck()) { + for (int i = 1; i < ac; ++i) { + e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); + } + + e->CallStaticVoidMethod(c, m, a); + } + } + } + } + + int exitCode = 0; + if (e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + } + + vm->DestroyJavaVM(); + + return exitCode; + } + EOF + + $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ + -D_JNI_IMPLEMENTATION_ -c bootimage-main.cpp -o main.o + +__8.__ Link the objects produced above to produce the final + executable, and optionally strip its symbols. + + $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello + $ strip --strip-all hello + + +Trademarks +---------- + +Oracle and Java are registered trademarks of Oracle and/or its +affiliates. Other names may be trademarks of their respective owners. + +The Avian project is not affiliated with Oracle. diff --git a/android.pro b/android.pro new file mode 100644 index 0000000000..cc4e597efd --- /dev/null +++ b/android.pro @@ -0,0 +1,75 @@ +# these are referenced in JniConstants.cpp: + +-keep class java.text.Bidi$Run +-keep class java.math.BigDecimal +-keep class java.lang.Boolean +-keep class java.lang.Byte +-keep class java.nio.charset.CharsetICU { + CharsetICU(java.lang.String, java.lang.String, java.lang.String[]); + } +-keep class java.lang.reflect.Constructor +-keep class java.util.zip.Deflater +-keep class java.lang.Double +-keep class libcore.io.ErrnoException +-keep class java.lang.reflect.Field +-keep class libcore.icu.NativeDecimalFormat$FieldPositionIterator { + void setData(int[]); + } +-keep class java.io.FileDescriptor +-keep class libcore.io.GaiException +-keep class java.net.Inet6Address +-keep class java.net.InetAddress +-keep class java.net.InetSocketAddress +-keep class java.util.zip.Inflater +-keep class java.lang.Integer +-keep class libcore.icu.LocaleData +-keep class java.lang.Long +-keep class java.lang.reflect.Method +-keep class libcore.util.MutableInt +-keep class libcore.util.MutableLong +-keep class java.text.ParsePosition +-keep class java.util.regex.PatternSyntaxException +-keep class java.lang.RealToString +-keep class java.net.Socket +-keep class java.net.SocketImpl +-keep class java.lang.String +-keep class libcore.io.StructAddrinfo +-keep class libcore.io.StructFlock +-keep class libcore.io.StructGroupReq +-keep class libcore.io.StructLinger +-keep class libcore.io.StructPasswd +-keep class libcore.io.StructPollfd +-keep class libcore.io.StructStat { + StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long); + } +-keep class libcore.io.StructStatFs +-keep class libcore.io.StructTimeval +-keep class libcore.io.StructUtsname { + StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String); + } + +# referenced from libcore native code + +-keep class libcore.icu.LocaleData { + ; + } + +# called from the VM + +-keep class java.lang.Thread { + Thread(java.lang.ThreadGroup, java.lang.String, int, boolean); + } + +-keep class avian.Classes { + java.lang.Class forName(java.lang.String, boolean, java.lang.ClassLoader); + int findField(avian.VMClass, java.lang.String); + int findMethod(avian.VMClass, java.lang.String, java.lang.Class[]); + java.lang.annotation.Annotation getAnnotation(java.lang.ClassLoader, java.lang.Object[]); + } + +-keep class java.lang.VMThread { + VMThread(java.lang.Thread); + } + +# loaded reflectively to handle embedded resources: +-keep class avian.avianvmresource.Handler diff --git a/classpath/avian/ClassAddendum.java b/classpath/avian/ClassAddendum.java index 48ac3bdd07..3d2a9addf2 100644 --- a/classpath/avian/ClassAddendum.java +++ b/classpath/avian/ClassAddendum.java @@ -14,4 +14,6 @@ public class ClassAddendum extends Addendum { public Object[] interfaceTable; public Object[] innerClassTable; public Object[] methodTable; + public Object enclosingClass; + public Object enclosingMethod; } diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index a445114022..a50118e1b3 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -16,6 +16,8 @@ import static avian.Stream.read2; import java.lang.reflect.Modifier; import java.lang.reflect.Method; import java.lang.reflect.Field; +import java.lang.reflect.Proxy; +import java.lang.annotation.Annotation; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -262,6 +264,159 @@ public class Classes { link(c, c.loader); } + public static Class forName(String name, boolean initialize, + ClassLoader loader) + throws ClassNotFoundException + { + if (loader == null) { + loader = Class.class.getClassLoader(); + } + Class c = loader.loadClass(name); + VMClass vmc = SystemClassLoader.vmClass(c); + Classes.link(vmc, loader); + if (initialize) { + Classes.initialize(vmc); + } + return c; + } + + public static Class forCanonicalName(String name) { + return forCanonicalName(null, name); + } + + public static Class forCanonicalName(ClassLoader loader, String name) { + try { + if (name.startsWith("[")) { + return forName(name, true, loader); + } else if (name.startsWith("L")) { + return forName(name.substring(1, name.length() - 1), true, loader); + } else { + if (name.length() == 1) { + return SystemClassLoader.getClass + (Classes.primitiveClass(name.charAt(0))); + } else { + throw new ClassNotFoundException(name); + } + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + private static int next(char c, String s, int start) { + for (int i = start; i < s.length(); ++i) { + if (s.charAt(i) == c) return i; + } + throw new RuntimeException(); + } + + public static Class[] getParameterTypes(VMMethod vmMethod) { + int count = vmMethod.parameterCount; + + Class[] types = new Class[count]; + int index = 0; + + String spec = new String + (vmMethod.spec, 1, vmMethod.spec.length - 2); + + try { + for (int i = 0; i < spec.length(); ++i) { + char c = spec.charAt(i); + if (c == ')') { + break; + } else if (c == 'L') { + int start = i + 1; + i = next(';', spec, start); + String name = spec.substring(start, i).replace('/', '.'); + types[index++] = Class.forName(name, true, vmMethod.class_.loader); + } else if (c == '[') { + int start = i; + while (spec.charAt(i) == '[') ++i; + + if (spec.charAt(i) == 'L') { + i = next(';', spec, i + 1); + String name = spec.substring(start, i).replace('/', '.'); + types[index++] = Class.forName + (name, true, vmMethod.class_.loader); + } else { + String name = spec.substring(start, i + 1); + types[index++] = forCanonicalName(vmMethod.class_.loader, name); + } + } else { + String name = spec.substring(i, i + 1); + types[index++] = forCanonicalName(vmMethod.class_.loader, name); + } + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + + return types; + } + + public static int findField(VMClass vmClass, String name) { + if (vmClass.fieldTable != null) { + Classes.link(vmClass); + + for (int i = 0; i < vmClass.fieldTable.length; ++i) { + if (toString(vmClass.fieldTable[i].name).equals(name)) { + return i; + } + } + } + return -1; + } + + public static String toString(byte[] array) { + return new String(array, 0, array.length - 1); + } + + public static boolean match(Class[] a, Class[] b) { + if (a.length == b.length) { + for (int i = 0; i < a.length; ++i) { + if (! a[i].isAssignableFrom(b[i])) { + return false; + } + } + return true; + } else { + return false; + } + } + + public static int findMethod(VMClass vmClass, String name, + Class[] parameterTypes) + { + if (vmClass.methodTable != null) { + Classes.link(vmClass); + + if (parameterTypes == null) { + parameterTypes = new Class[0]; + } + + for (int i = 0; i < vmClass.methodTable.length; ++i) { + if (toString(vmClass.methodTable[i].name).equals(name) + && match(parameterTypes, + getParameterTypes(vmClass.methodTable[i]))) + { + return i; + } + } + } + return -1; + } + + public static Annotation getAnnotation(ClassLoader loader, Object[] a) { + if (a[0] == null) { + a[0] = Proxy.newProxyInstance + (loader, new Class[] { (Class) a[1] }, + new AnnotationInvocationHandler(a)); + } + return (Annotation) a[0]; + } + + public static native Method makeMethod(Class c, int slot); + private static native void acquireClassLock(); private static native void releaseClassLock(); diff --git a/classpath/avian/OpenJDK.java b/classpath/avian/OpenJDK.java index 95248b2398..7c60d7238d 100644 --- a/classpath/avian/OpenJDK.java +++ b/classpath/avian/OpenJDK.java @@ -45,20 +45,4 @@ public class OpenJDK { } return array; } - - public static Class getDeclaringClass(VMClass c) { - try { - String name = new String - (replace('/', '.', c.name, 0, c.name.length - 1), 0, - c.name.length - 1); - int index = name.lastIndexOf("$"); - if (index == -1) { - return null; - } else { - return c.loader.loadClass(name.substring(0, index)); - } - } catch (ClassNotFoundException e) { - return null; - } - } } diff --git a/classpath/avian/PersistentSet.java b/classpath/avian/PersistentSet.java index 5683327aee..917607ed90 100644 --- a/classpath/avian/PersistentSet.java +++ b/classpath/avian/PersistentSet.java @@ -252,7 +252,7 @@ public class PersistentSet implements Iterable { } ancestors.next = new Cell(n, ancestors.next); - sibling = ancestors.value.right; + sibling = ancestors.value.right = new Node(ancestors.value.right); } if (! (sibling.left.red || sibling.right.red)) { @@ -303,7 +303,7 @@ public class PersistentSet implements Iterable { } ancestors.next = new Cell(n, ancestors.next); - sibling = ancestors.value.left; + sibling = ancestors.value.left = new Node(ancestors.value.left); } if (! (sibling.right.red || sibling.left.red)) { diff --git a/classpath/avian/SystemClassLoader.java b/classpath/avian/SystemClassLoader.java index 5c228cd9c1..db704c6c26 100644 --- a/classpath/avian/SystemClassLoader.java +++ b/classpath/avian/SystemClassLoader.java @@ -28,6 +28,8 @@ public class SystemClassLoader extends ClassLoader { public static native Class getClass(VMClass vmClass); + public static native VMClass vmClass(Class jClass); + private native VMClass findLoadedVMClass(String name); protected Class reallyFindLoadedClass(String name){ @@ -35,6 +37,30 @@ public class SystemClassLoader extends ClassLoader { return c == null ? null : getClass(c); } + protected Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + Class c = findLoadedClass(name); + if (c == null) { + ClassLoader parent = getParent(); + if (parent != null) { + try { + c = parent.loadClass(name); + } catch (ClassNotFoundException ok) { } + } + + if (c == null) { + c = findClass(name); + } + } + + if (resolve) { + resolveClass(c); + } + + return c; + } + private native String resourceURLPrefix(String name); protected URL findResource(String name) { diff --git a/classpath/avian/Traces.java b/classpath/avian/Traces.java new file mode 100644 index 0000000000..14f7ef7d15 --- /dev/null +++ b/classpath/avian/Traces.java @@ -0,0 +1,61 @@ +package avian; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +public class Traces { + private static final String Newline = System.getProperty("line.separator"); + + private static String traceAllThreads() { + StringBuilder buffer = new StringBuilder(); + + Thread[] threads = new Thread[Thread.activeCount()]; + + int count = Thread.enumerate(threads); + for (int i = 0; i < count; ++i) { + traceThread(threads[i], buffer); + } + + return buffer.toString(); + } + + private static String traceThread(Thread thread) { + StringBuilder buffer = new StringBuilder(); + + traceThread(thread, buffer); + + return buffer.toString(); + } + + private static void traceThread(Thread thread, StringBuilder buffer) { + buffer.append(thread).append(Newline); + for (StackTraceElement e: thread.getStackTrace()) { + buffer.append("\tat ").append(e).append(Newline); + } + } + + public static void startTraceListener(final String host, final int port) { + Thread t = new Thread(new Runnable() { + public void run() { + try { + ServerSocketChannel server = ServerSocketChannel.open(); + server.socket().bind(new InetSocketAddress(host, port)); + while (true) { + SocketChannel c = server.accept(); + try { + c.write(ByteBuffer.wrap(traceAllThreads().getBytes())); + } finally { + c.close(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + t.setDaemon(true); + t.start(); + } +} diff --git a/classpath/avian/avian_vm_resource/Handler.java b/classpath/avian/avianvmresource/Handler.java similarity index 98% rename from classpath/avian/avian_vm_resource/Handler.java rename to classpath/avian/avianvmresource/Handler.java index dfad1f7964..ea776c7e92 100644 --- a/classpath/avian/avian_vm_resource/Handler.java +++ b/classpath/avian/avianvmresource/Handler.java @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -package avian.avian_vm_resource; +package avian.avianvmresource; import java.net.URL; import java.net.URLStreamHandler; diff --git a/classpath/java-io.cpp b/classpath/java-io.cpp index 86020f57c8..ff6bc8fffc 100644 --- a/classpath/java-io.cpp +++ b/classpath/java-io.cpp @@ -55,6 +55,15 @@ typedef wchar_t char_t; +#if defined(WINAPI_FAMILY) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#include "avian-interop.h" +#define SKIP_OPERATOR_NEW + +#endif +#endif + #else // not PLATFORM_WINDOWS # include @@ -83,7 +92,19 @@ typedef char char_t; #endif // not PLATFORM_WINDOWS +#ifndef WINAPI_FAMILY +# ifndef WINAPI_PARTITION_DESKTOP +# define WINAPI_PARTITION_DESKTOP 1 +# endif + +# ifndef WINAPI_FAMILY_PARTITION +# define WINAPI_FAMILY_PARTITION(x) (x) +# endif +#endif // WINAPI_FAMILY + +#if !defined(SKIP_OPERATOR_NEW) inline void* operator new(size_t, void* p) throw() { return p; } +#endif typedef const char_t* string_t; @@ -155,69 +176,9 @@ doWrite(JNIEnv* e, jint fd, const jbyte* data, jint length) } } + #ifdef PLATFORM_WINDOWS -class Mapping { - public: - Mapping(uint8_t* start, size_t length, HANDLE mapping, HANDLE file): - start(start), - length(length), - mapping(mapping), - file(file) - { } - - uint8_t* start; - size_t length; - HANDLE mapping; - HANDLE file; -}; - -inline Mapping* -map(JNIEnv* e, string_t path) -{ - Mapping* result = 0; - HANDLE file = CreateFileW(path, FILE_READ_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE, 0, - OPEN_EXISTING, 0, 0); - if (file != INVALID_HANDLE_VALUE) { - unsigned size = GetFileSize(file, 0); - if (size != INVALID_FILE_SIZE) { - HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0); - if (mapping) { - void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); - if (data) { - void* p = allocate(e, sizeof(Mapping)); - if (not e->ExceptionCheck()) { - result = new (p) - Mapping(static_cast(data), size, file, mapping); - } - } - - if (result == 0) { - CloseHandle(mapping); - } - } - } - - if (result == 0) { - CloseHandle(file); - } - } - if (result == 0 and not e->ExceptionCheck()) { - throwNew(e, "java/io/IOException", "%d", GetLastError()); - } - return result; -} - -inline void -unmap(JNIEnv*, Mapping* mapping) -{ - UnmapViewOfFile(mapping->start); - CloseHandle(mapping->mapping); - CloseHandle(mapping->file); - free(mapping); -} - class Directory { public: Directory(): handle(0), findNext(false) { } @@ -250,51 +211,9 @@ class Directory { #else // not PLATFORM_WINDOWS -class Mapping { - public: - Mapping(uint8_t* start, size_t length): - start(start), - length(length) - { } - - uint8_t* start; - size_t length; -}; - -inline Mapping* -map(JNIEnv* e, string_t path) -{ - Mapping* result = 0; - int fd = open(path, O_RDONLY); - if (fd != -1) { - struct stat s; - int r = fstat(fd, &s); - if (r != -1) { - void* data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (data) { - void* p = allocate(e, sizeof(Mapping)); - if (not e->ExceptionCheck()) { - result = new (p) Mapping(static_cast(data), s.st_size); - } - } - } - close(fd); - } - if (result == 0 and not e->ExceptionCheck()) { - throwNewErrno(e, "java/io/IOException"); - } - return result; -} - -inline void -unmap(JNIEnv*, Mapping* mapping) -{ - munmap(mapping->start, mapping->length); - free(mapping); -} - #endif // not PLATFORM_WINDOWS + } // namespace inline string_t getChars(JNIEnv* e, jstring path) { @@ -316,6 +235,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_java_io_File_toAbsolutePath(JNIEnv* e UNUSED, jclass, jstring path) { #ifdef PLATFORM_WINDOWS +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) string_t chars = getChars(e, path); if (chars) { const unsigned BufferSize = MAX_PATH; @@ -330,6 +250,19 @@ Java_java_io_File_toAbsolutePath(JNIEnv* e UNUSED, jclass, jstring path) } return path; +# else + string_t chars = getChars(e, path); + if(chars) { + std::wstring partialPath = chars; + releaseChars(e, path, chars); + + std::wstring fullPath = AvianInterop::GetFullPath(partialPath); + + return e->NewString + (reinterpret_cast(fullPath.c_str()), fullPath.length()); + } + return path; +# endif #else jstring result = path; string_t chars = getChars(e, path); @@ -353,20 +286,41 @@ Java_java_io_File_toAbsolutePath(JNIEnv* e UNUSED, jclass, jstring path) extern "C" JNIEXPORT jlong JNICALL Java_java_io_File_length(JNIEnv* e, jclass, jstring path) { - #ifdef PLATFORM_WINDOWS - - LARGE_INTEGER fileSize; + // Option: without opening file + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx string_t chars = getChars(e, path); - HANDLE file = CreateFileW - (chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); - releaseChars(e, path, chars); - if (file != INVALID_HANDLE_VALUE) - GetFileSizeEx(file, &fileSize); - else return 0; - CloseHandle(file); - return static_cast(fileSize.QuadPart); + if(chars) { + LARGE_INTEGER fileSize; + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE file = CreateFileW + (chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + #else + HANDLE file = CreateFile2 + (chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + #endif + releaseChars(e, path, chars); + if (file == INVALID_HANDLE_VALUE) + return 0; + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if(!GetFileSizeEx(file, &fileSize)) + { + CloseHandle(file); + return 0; + } + #else + FILE_STANDARD_INFO info; + if(!GetFileInformationByHandleEx(file, FileStandardInfo, &info, sizeof(info))) + { + CloseHandle(file); + return 0; + } + fileSize = info.EndOfFile; + #endif + CloseHandle(file); + return static_cast(fileSize.QuadPart); + } #else string_t chars = getChars(e, path); @@ -598,7 +552,11 @@ Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path) releaseChars(e, path, chars); Directory* d = new (malloc(sizeof(Directory))) Directory; + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) d->handle = FindFirstFileW(RUNTIME_ARRAY_BODY(buffer), &(d->data)); + #else + d->handle = FindFirstFileExW(RUNTIME_ARRAY_BODY(buffer), FindExInfoStandard, &(d->data), FindExSearchNameMatch, NULL, 0); + #endif if (d->handle == INVALID_HANDLE_VALUE) { d->dispose(); d = 0; @@ -610,6 +568,62 @@ Java_java_io_File_openDir(JNIEnv* e, jclass, jstring path) } } +extern "C" JNIEXPORT jlong JNICALL +Java_java_io_File_lastModified(JNIEnv* e, jclass, jstring path) +{ + string_t chars = getChars(e, path); + if (chars) { + #ifdef PLATFORM_WINDOWS + // Option: without opening file + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946(v=vs.85).aspx + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE hFile = CreateFileW + (chars, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + #else + HANDLE hFile = CreateFile2 + (chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + #endif + releaseChars(e, path, chars); + if (hFile == INVALID_HANDLE_VALUE) + return 0; + LARGE_INTEGER fileDate, filetimeToUnixEpochAdjustment; + filetimeToUnixEpochAdjustment.QuadPart = 11644473600000L * 10000L; + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + FILETIME fileLastWriteTime; + if (!GetFileTime(hFile, 0, 0, &fileLastWriteTime)) + { + CloseHandle(hFile); + return 0; + } + fileDate.HighPart = fileLastWriteTime.dwHighDateTime; + fileDate.LowPart = fileLastWriteTime.dwLowDateTime; + #else + FILE_BASIC_INFO fileInfo; + if (!GetFileInformationByHandleEx(hFile, FileBasicInfo, &fileInfo, sizeof(fileInfo))) + { + CloseHandle(hFile); + return 0; + } + fileDate = fileInfo.ChangeTime; + #endif + CloseHandle(hFile); + fileDate.QuadPart -= filetimeToUnixEpochAdjustment.QuadPart; + return fileDate.QuadPart / 10000000L; + #else + struct stat fileStat; + if (stat(chars, &fileStat) == -1) { + releaseChars(e, path, chars); + return 0; + } + + return (static_cast(st.st_mtim.tv_sec) * 1000) + + (static_cast(st.st_mtim.tv_nsec) / (1000*1000)); + #endif + } + + return 0; +} + extern "C" JNIEXPORT jstring JNICALL Java_java_io_File_readDir(JNIEnv* e, jclass, jlong handle) { @@ -759,13 +773,13 @@ Java_java_io_FileOutputStream_write__I_3BII (JNIEnv* e, jclass, jint fd, jbyteArray b, jint offset, jint length) { jbyte* data = static_cast(malloc(length)); + if (data == 0) { throwNew(e, "java/lang/OutOfMemoryError", 0); return; } e->GetByteArrayRegion(b, offset, length, data); - if (not e->ExceptionCheck()) { doWrite(e, fd, data, length); } @@ -785,35 +799,104 @@ Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path, { string_t chars = getChars(e, path); if (chars) { - Mapping* mapping = map(e, chars); + jlong peer = 0; + jlong length = 0; + #if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + #if defined(PLATFORM_WINDOWS) + int fd = ::_wopen(chars, O_RDONLY | OPEN_MASK); + #else + int fd = ::open((const char*)chars, O_RDONLY | OPEN_MASK); + #endif + releaseChars(e, path, chars); + if (fd == -1) { + throwNewErrno(e, "java/io/IOException"); + return; + } + struct ::stat fileStats; + if(::fstat(fd, &fileStats) == -1) { + ::close(fd); + throwNewErrno(e, "java/io/IOException"); + return; + } + peer = fd; + length = fileStats.st_size; + #else + HANDLE hFile = CreateFile2 + (chars, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + throwNewErrno(e, "java/io/IOException"); + return; + } + + FILE_STANDARD_INFO info; + if(!GetFileInformationByHandleEx(hFile, FileStandardInfo, &info, sizeof(info))) { + CloseHandle(hFile); + throwNewErrno(e, "java/io/IOException"); + return; + } + + peer = (jlong)hFile; + length = info.EndOfFile.QuadPart; + #endif - jlong peer = reinterpret_cast(mapping); e->SetLongArrayRegion(result, 0, 1, &peer); - - jlong length = (mapping ? mapping->length : 0); e->SetLongArrayRegion(result, 1, 1, &length); - - releaseChars(e, path, chars); } } -extern "C" JNIEXPORT void JNICALL -Java_java_io_RandomAccessFile_copy(JNIEnv* e, jclass, jlong peer, +extern "C" JNIEXPORT jint JNICALL +Java_java_io_RandomAccessFile_readBytes(JNIEnv* e, jclass, jlong peer, jlong position, jbyteArray buffer, int offset, int length) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + if(::lseek(fd, position, SEEK_SET) == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + uint8_t* dst = reinterpret_cast (e->GetPrimitiveArrayCritical(buffer, 0)); - memcpy(dst + offset, - reinterpret_cast(peer)->start + position, - length); - + int64_t bytesRead = ::read(fd, dst + offset, length); e->ReleasePrimitiveArrayCritical(buffer, dst, 0); + + if(bytesRead == -1) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } +#else + HANDLE hFile = (HANDLE)peer; + LARGE_INTEGER lPos; + lPos.QuadPart = position; + if(!SetFilePointerEx(hFile, lPos, nullptr, FILE_BEGIN)) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + + uint8_t* dst = reinterpret_cast + (e->GetPrimitiveArrayCritical(buffer, 0)); + + DWORD bytesRead = 0; + if(!ReadFile(hFile, dst + offset, length, &bytesRead, nullptr)) { + throwNewErrno(e, "java/io/IOException"); + return -1; + } + e->ReleasePrimitiveArrayCritical(buffer, dst, 0); +#endif + + return (jint)bytesRead; } extern "C" JNIEXPORT void JNICALL -Java_java_io_RandomAccessFile_close(JNIEnv* e, jclass, jlong peer) +Java_java_io_RandomAccessFile_close(JNIEnv* /* e*/, jclass, jlong peer) { - unmap(e, reinterpret_cast(peer)); +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int fd = (int)peer; + ::close(fd); +#else + HANDLE hFile = (HANDLE)peer; + CloseHandle(hFile); +#endif } diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index 865f3c3fb4..88a4cf5ef6 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -54,33 +54,64 @@ # include "signal.h" # include "sys/time.h" # include "sys/types.h" +# ifndef __ANDROID__ # include "sys/sysctl.h" +# endif # include "sys/utsname.h" # include "sys/wait.h" #endif // not PLATFORM_WINDOWS +#ifndef WINAPI_FAMILY +# ifndef WINAPI_PARTITION_DESKTOP +# define WINAPI_PARTITION_DESKTOP 1 +# endif + +# ifndef WINAPI_FAMILY_PARTITION +# define WINAPI_FAMILY_PARTITION(x) (x) +# endif +#else +# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +# include "avian-interop.h" + +# endif +#endif // WINAPI_FAMILY + namespace { #ifdef PLATFORM_WINDOWS - char* getErrorStr(DWORD err){ - // The poor man's error string, just print the error code - char * errStr = (char*) malloc(9 * sizeof(char)); - snprintf(errStr, 9, "%d", (int) err); - return errStr; - - // The better way to do this, if I could figure out how to convert LPTSTR to char* - //char* errStr; - //LPTSTR s; - //if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - // FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, &s, 0, NULL) == 0) - //{ - // errStr.Format("Unknown error occurred (%08x)", err); - //} else { - // errStr = s; - //} - //return errStr; - } +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + char* getErrorStr(DWORD err) { + LPSTR errorStr = 0; + if(!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, LANG_SYSTEM_DEFAULT, (LPSTR)&errorStr, 0, 0)) + { + char* errStr = (char*) malloc(9 * sizeof(char)); + snprintf(errStr, 9, "%d", (int) err); + return errStr; + } + char* errStr = strdup(errorStr); + LocalFree(errorStr); + return errStr; + } +#else + char* getErrorStr(DWORD err) { + LPSTR errorStr = (LPSTR)malloc(4096); //NOTE: something constant + if(!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err, LANG_SYSTEM_DEFAULT, errorStr, 0, 0)) + { + free(errorStr); + + char* errStr = (char*) malloc(9 * sizeof(char)); + snprintf(errStr, 9, "%d", (int) err); + return errStr; + } + char* errStr = strdup(errorStr); + free(errorStr); + return errStr; + } +#endif + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) void makePipe(JNIEnv* e, HANDLE p[2]) { SECURITY_ATTRIBUTES sa; @@ -93,6 +124,7 @@ namespace { throwNew(e, "java/io/IOException", getErrorStr(GetLastError())); } } +#endif int descriptor(JNIEnv* e, HANDLE h) { @@ -194,7 +226,7 @@ extern "C" JNIEXPORT void JNICALL Java_java_lang_Runtime_exec(JNIEnv* e, jclass, jobjectArray command, jlongArray process) { - +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) int size = 0; for (int i = 0; i < e->GetArrayLength(command); ++i){ jstring element = (jstring) e->GetObjectArrayElement(command, i); @@ -265,11 +297,15 @@ Java_java_lang_Runtime_exec(JNIEnv* e, jclass, e->SetLongArrayRegion(process, 0, 1, &pid); jlong tid = reinterpret_cast(pi.hThread); e->SetLongArrayRegion(process, 1, 1, &tid); +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); +#endif } extern "C" JNIEXPORT jint JNICALL Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) DWORD exitCode; WaitForSingleObject(reinterpret_cast(pid), INFINITE); BOOL success = GetExitCodeProcess(reinterpret_cast(pid), &exitCode); @@ -281,14 +317,23 @@ Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) CloseHandle(reinterpret_cast(tid)); return exitCode; +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); + return -1; +#endif } extern "C" JNIEXPORT void JNICALL -Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) { +Java_java_lang_Runtime_kill(JNIEnv* e UNUSED, jclass, jlong pid) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) TerminateProcess(reinterpret_cast(pid), 1); +#else + throwNew(e, "java/io/Exception", strdup("Not supported on WinRT/WinPhone8")); +#endif } Locale getLocale() { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) const char* lang = ""; const char* reg = ""; unsigned langid = GetUserDefaultUILanguage(); @@ -360,8 +405,23 @@ Locale getLocale() { default: lang = "en"; } + return Locale(lang, reg); +#else + std::wstring culture = AvianInterop::GetCurrentUICulture(); + char* cultureName = strdup(std::string(culture.begin(), culture.end()).c_str()); + char* delimiter = strchr(cultureName, '-'); + if(!delimiter) + { + free(cultureName); + return Locale("en", "US"); + } + const char* lang = cultureName; + const char* reg = delimiter + 1; + *delimiter = 0; Locale locale(lang, reg); + free(cultureName); return locale; +#endif } #else extern "C" JNIEXPORT void JNICALL @@ -529,8 +589,15 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, } else if (strcmp(chars, "file.separator") == 0) { r = e->NewStringUTF("\\"); } else if (strcmp(chars, "os.name") == 0) { +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) r = e->NewStringUTF("Windows"); +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) + r = e->NewStringUTF("Windows Phone"); +# else + r = e->NewStringUTF("Windows RT"); +# endif } else if (strcmp(chars, "os.version") == 0) { +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) unsigned size = 32; RUNTIME_ARRAY(char, buffer, size); OSVERSIONINFO OSversion; @@ -538,6 +605,10 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, ::GetVersionEx(&OSversion); snprintf(RUNTIME_ARRAY_BODY(buffer), size, "%i.%i", (int)OSversion.dwMajorVersion, (int)OSversion.dwMinorVersion); r = e->NewStringUTF(RUNTIME_ARRAY_BODY(buffer)); +# else + // Currently there is no alternative on WinRT/WP8 + r = e->NewStringUTF("8.0"); +# endif } else if (strcmp(chars, "os.arch") == 0) { #ifdef ARCH_x86_32 r = e->NewStringUTF("x86"); @@ -549,15 +620,26 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, r = e->NewStringUTF("arm"); #endif } else if (strcmp(chars, "java.io.tmpdir") == 0) { +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) TCHAR buffer[MAX_PATH]; GetTempPath(MAX_PATH, buffer); r = e->NewStringUTF(buffer); +# else + std::wstring tmpDir = AvianInterop::GetTemporaryFolder(); + r = e->NewString((const jchar*)tmpDir.c_str(), tmpDir.length()); +# endif } else if (strcmp(chars, "user.dir") == 0) { +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) TCHAR buffer[MAX_PATH]; GetCurrentDirectory(MAX_PATH, buffer); r = e->NewStringUTF(buffer); +# else + std::wstring userDir = AvianInterop::GetInstalledLocation(); + r = e->NewString((const jchar*)userDir.c_str(), userDir.length()); +# endif } else if (strcmp(chars, "user.home") == 0) { # ifdef _MSC_VER +# if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) WCHAR buffer[MAX_PATH]; size_t needed; if (_wgetenv_s(&needed, buffer, MAX_PATH, L"USERPROFILE") == 0) { @@ -565,6 +647,10 @@ Java_java_lang_System_getProperty(JNIEnv* e, jclass, jstring name, } else { r = 0; } +# else + std::wstring userHome = AvianInterop::GetDocumentsLibraryLocation(); + r = e->NewString((const jchar*)userHome.c_str(), userHome.length()); +# endif # else LPWSTR home = _wgetenv(L"USERPROFILE"); r = e->NewString(reinterpret_cast(home), lstrlenW(home)); @@ -652,6 +738,9 @@ namespace { #elif defined __APPLE__ # include # define environ (*_NSGetEnviron()) +#elif defined(WINAPI_FAMILY) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// WinRT/WP8 does not provide alternative for environment variables +char* environ[] = { 0 }; #else extern char** environ; #endif @@ -785,6 +874,54 @@ Java_java_lang_Math_cos(JNIEnv*, jclass, jdouble val) return cos(val); } +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_tan(JNIEnv*, jclass, jdouble val) +{ + return tan(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_asin(JNIEnv*, jclass, jdouble val) +{ + return asin(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_acos(JNIEnv*, jclass, jdouble val) +{ + return acos(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_atan(JNIEnv*, jclass, jdouble val) +{ + return atan(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_atan2(JNIEnv*, jclass, jdouble y, jdouble x) +{ + return atan2(y, x); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_sinh(JNIEnv*, jclass, jdouble val) +{ + return sinh(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_cosh(JNIEnv*, jclass, jdouble val) +{ + return cosh(val); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_tanh(JNIEnv*, jclass, jdouble val) +{ + return tanh(val); +} + extern "C" JNIEXPORT jdouble JNICALL Java_java_lang_Math_sqrt(JNIEnv*, jclass, jdouble val) { @@ -797,6 +934,12 @@ Java_java_lang_Math_pow(JNIEnv*, jclass, jdouble val, jdouble exp) return pow(val, exp); } +extern "C" JNIEXPORT jdouble JNICALL +Java_java_lang_Math_log(JNIEnv*, jclass, jdouble val) +{ + return log(val); +} + extern "C" JNIEXPORT jdouble JNICALL Java_java_lang_Math_floor(JNIEnv*, jclass, jdouble val) { diff --git a/classpath/java-nio.cpp b/classpath/java-nio.cpp index d5e067e1e4..5324c7b444 100644 --- a/classpath/java-nio.cpp +++ b/classpath/java-nio.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2011, Avian Contributors +/* Copyright (c) 2008-2012, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -262,18 +262,32 @@ setTcpNoDelay(JNIEnv* e, int d, bool on) void doBind(JNIEnv* e, int s, sockaddr_in* address) { - int opt = 1; - int r = ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - reinterpret_cast(&opt), sizeof(int)); - if (r != 0) { - throwIOException(e); - return; + { int opt = 1; + int r = ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + reinterpret_cast(&opt), sizeof(int)); + if (r != 0) { + throwIOException(e); + return; + } } - r = ::bind(s, reinterpret_cast(address), sizeof(sockaddr_in)); - if (r != 0) { - throwIOException(e); - return; +#ifdef SO_NOSIGPIPE + { int opt = 1; + int r = ::setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, + reinterpret_cast(&opt), sizeof(int)); + if (r != 0) { + throwIOException(e); + return; + } + } +#endif + + { int r = ::bind + (s, reinterpret_cast(address), sizeof(sockaddr_in)); + if (r != 0) { + throwIOException(e); + return; + } } } @@ -1015,3 +1029,15 @@ Java_java_nio_channels_SocketSelector_natUpdateReadySet(JNIEnv *, jclass, } +extern "C" JNIEXPORT jboolean JNICALL +Java_java_nio_ByteOrder_isNativeBigEndian(JNIEnv *, jclass) +{ + union { + uint32_t i; + char c[4]; + } u = {0x01020304}; + + if (u.c[0] == 1) + return JNI_TRUE; + return JNI_FALSE; +} diff --git a/classpath/java-util-zip.cpp b/classpath/java-util-zip.cpp index 6fb471bdfd..796156d57b 100644 --- a/classpath/java-util-zip.cpp +++ b/classpath/java-util-zip.cpp @@ -10,7 +10,7 @@ #include "stdlib.h" #include "string.h" -#include "zlib-custom.h" +#include "avian/zlib-custom.h" #include "jni.h" #include "jni-util.h" diff --git a/classpath/java/io/Closeable.java b/classpath/java/io/Closeable.java new file mode 100644 index 0000000000..e24a572fb5 --- /dev/null +++ b/classpath/java/io/Closeable.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2012, 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.io; + +public interface Closeable { + void close() + throws IOException; +} diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 187549ebfd..8eceed4b71 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, Avian Contributors +/* Copyright (c) 2008-2013, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -16,6 +16,11 @@ public class File implements Serializable { public static final String separator = FileSeparator; + private static final String PathSeparator + = System.getProperty("path.separator"); + + public static final String pathSeparator = PathSeparator; + // static { // System.loadLibrary("natives"); // } @@ -289,12 +294,19 @@ public class File implements Serializable { } } + public long lastModified() { + return lastModified(path); + } private static native long openDir(String path); + private static native long lastModified(String path); + private static native String readDir(long handle); private static native long closeDir(long handle); + + private static class Pair { public final String value; public final Pair next; diff --git a/classpath/java/io/Flushable.java b/classpath/java/io/Flushable.java new file mode 100644 index 0000000000..25b941d4bb --- /dev/null +++ b/classpath/java/io/Flushable.java @@ -0,0 +1,16 @@ +/* Copyright (c) 2012, 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.io; + +public interface Flushable { + void flush() + throws IOException; +} diff --git a/classpath/java/io/InputStream.java b/classpath/java/io/InputStream.java index ec29b52b7a..d6ab46c140 100644 --- a/classpath/java/io/InputStream.java +++ b/classpath/java/io/InputStream.java @@ -10,7 +10,7 @@ package java.io; -public abstract class InputStream { +public abstract class InputStream implements Closeable { public abstract int read() throws IOException; public int read(byte[] buffer) throws IOException { diff --git a/classpath/java/io/OutputStream.java b/classpath/java/io/OutputStream.java index caeccde12c..b73345ee16 100644 --- a/classpath/java/io/OutputStream.java +++ b/classpath/java/io/OutputStream.java @@ -10,7 +10,7 @@ package java.io; -public abstract class OutputStream { +public abstract class OutputStream implements Closeable, Flushable { public abstract void write(int c) throws IOException; public void write(byte[] buffer) throws IOException { diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index b6e88dd00e..da9a8356b6 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -10,6 +10,8 @@ package java.io; +import java.lang.IllegalArgumentException; + public class RandomAccessFile { private long peer; private File file; @@ -56,26 +58,68 @@ public class RandomAccessFile { this.position = position; } - public void readFully(byte[] buffer, int offset, int length) - throws IOException - { - if (peer == 0) throw new IOException(); - - if (length == 0) return; - - if (position + length > this.length) { - if (position + length > length()) throw new EOFException(); - } - - if (offset < 0 || offset + length > buffer.length) + public int skipBytes(int count) throws IOException { + if (position + count > length()) throw new IOException(); + this.position = position + count; + return count; + } + + public int read(byte b[], int off, int len) throws IOException { + if(b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(len == 0) + return 0; + if (position + len > this.length) + throw new EOFException(); + if (off < 0 || off + len > b.length) throw new ArrayIndexOutOfBoundsException(); - - copy(peer, position, buffer, offset, length); - - position += length; + int bytesRead = readBytes(peer, position, b, off, len); + position += bytesRead; + return bytesRead; + } + + public int read(byte b[]) throws IOException { + if(b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(b.length == 0) + return 0; + if (position + b.length > this.length) + throw new EOFException(); + int bytesRead = readBytes(peer, position, b, 0, b.length); + position += bytesRead; + return bytesRead; } - private static native void copy(long peer, long position, byte[] buffer, + public void readFully(byte b[], int off, int len) throws IOException { + if(b == null) + throw new IllegalArgumentException(); + if (peer == 0) + throw new IOException(); + if(len == 0) + return; + if (position + len > this.length) + throw new EOFException(); + if (off < 0 || off + len > b.length) + throw new ArrayIndexOutOfBoundsException(); + int n = 0; + do { + int count = readBytes(peer, position, b, off + n, len - n); + position += count; + if (count == 0) + throw new EOFException(); + n += count; + } while (n < len); + } + + public void readFully(byte b[]) throws IOException { + readFully(b, 0, b.length); + } + + private static native int readBytes(long peer, long position, byte[] buffer, int offset, int length); public void close() throws IOException { diff --git a/classpath/java/io/Reader.java b/classpath/java/io/Reader.java index 2edfd70803..668f82b1cf 100644 --- a/classpath/java/io/Reader.java +++ b/classpath/java/io/Reader.java @@ -10,7 +10,7 @@ package java.io; -public abstract class Reader { +public abstract class Reader implements Closeable { public int read() throws IOException { char[] buffer = new char[1]; int c = read(buffer); diff --git a/classpath/java/io/Writer.java b/classpath/java/io/Writer.java index 0d5b8fbf85..2ea1bcba07 100644 --- a/classpath/java/io/Writer.java +++ b/classpath/java/io/Writer.java @@ -10,7 +10,7 @@ package java.io; -public abstract class Writer { +public abstract class Writer implements Closeable, Flushable { public void write(int c) throws IOException { char[] buffer = new char[] { (char) c }; write(buffer); diff --git a/classpath/java/lang/Boolean.java b/classpath/java/lang/Boolean.java index ddd3bec725..be8b57ddcb 100644 --- a/classpath/java/lang/Boolean.java +++ b/classpath/java/lang/Boolean.java @@ -11,7 +11,7 @@ package java.lang; public final class Boolean implements Comparable { - public static final Class TYPE = Class.forCanonicalName("Z"); + public static final Class TYPE = avian.Classes.forCanonicalName("Z"); public static final Boolean FALSE = new Boolean(false); public static final Boolean TRUE = new Boolean(true); diff --git a/classpath/java/lang/Byte.java b/classpath/java/lang/Byte.java index 1133d6ec03..460ff0a51f 100644 --- a/classpath/java/lang/Byte.java +++ b/classpath/java/lang/Byte.java @@ -11,7 +11,7 @@ package java.lang; public final class Byte extends Number implements Comparable { - public static final Class TYPE = Class.forCanonicalName("B"); + public static final Class TYPE = avian.Classes.forCanonicalName("B"); private final byte value; diff --git a/classpath/java/lang/Character.java b/classpath/java/lang/Character.java index 3e7f1ed269..1cbeb1cea7 100644 --- a/classpath/java/lang/Character.java +++ b/classpath/java/lang/Character.java @@ -14,7 +14,7 @@ public final class Character implements Comparable { public static final int MIN_RADIX = 2; public static final int MAX_RADIX = 36; - public static final Class TYPE = Class.forCanonicalName("C"); + public static final Class TYPE = avian.Classes.forCanonicalName("C"); private final char value; diff --git a/classpath/java/lang/Class.java b/classpath/java/lang/Class.java index eb183260d6..baebcae4c9 100644 --- a/classpath/java/lang/Class.java +++ b/classpath/java/lang/Class.java @@ -37,7 +37,8 @@ import java.security.Permissions; import java.security.AllPermission; public final class Class implements Type, AnnotatedElement { - private static final int PrimitiveFlag = 1 << 5; + private static final int PrimitiveFlag = 1 << 5; + private static final int EnumFlag = 1 << 14; public final VMClass vmClass; @@ -144,38 +145,7 @@ public final class Class implements Type, AnnotatedElement { ClassLoader loader) throws ClassNotFoundException { - if (loader == null) { - loader = Class.class.vmClass.loader; - } - Class c = loader.loadClass(name); - Classes.link(c.vmClass, loader); - if (initialize) { - Classes.initialize(c.vmClass); - } - return c; - } - - public static Class forCanonicalName(String name) { - return forCanonicalName(null, name); - } - - public static Class forCanonicalName(ClassLoader loader, String name) { - try { - if (name.startsWith("[")) { - return forName(name, true, loader); - } else if (name.startsWith("L")) { - return forName(name.substring(1, name.length() - 1), true, loader); - } else { - if (name.length() == 1) { - return SystemClassLoader.getClass - (Classes.primitiveClass(name.charAt(0))); - } else { - throw new ClassNotFoundException(name); - } - } - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } + return Classes.forName(name, initialize, loader); } public Class getComponentType() { @@ -210,84 +180,36 @@ public final class Class implements Type, AnnotatedElement { return Classes.isAssignableFrom(vmClass, c.vmClass); } - private static Field findField(VMClass vmClass, String name) { - if (vmClass.fieldTable != null) { - Classes.link(vmClass); - - for (int i = 0; i < vmClass.fieldTable.length; ++i) { - if (Field.getName(vmClass.fieldTable[i]).equals(name)) { - return new Field(vmClass.fieldTable[i]); - } - } - } - return null; - } - public Field getDeclaredField(String name) throws NoSuchFieldException { - Field f = findField(vmClass, name); - if (f == null) { + int index = Classes.findField(vmClass, name); + if (index < 0) { throw new NoSuchFieldException(name); } else { - return f; + return new Field(vmClass.fieldTable[index]); } } public Field getField(String name) throws NoSuchFieldException { for (VMClass c = vmClass; c != null; c = c.super_) { - Field f = findField(c, name); - if (f != null) { - return f; + int index = Classes.findField(c, name); + if (index >= 0) { + return new Field(vmClass.fieldTable[index]); } } throw new NoSuchFieldException(name); } - private static boolean match(Class[] a, Class[] b) { - if (a.length == b.length) { - for (int i = 0; i < a.length; ++i) { - if (! a[i].isAssignableFrom(b[i])) { - return false; - } - } - return true; - } else { - return false; - } - } - - private static Method findMethod(VMClass vmClass, String name, - Class[] parameterTypes) - { - if (vmClass.methodTable != null) { - Classes.link(vmClass); - - if (parameterTypes == null) { - parameterTypes = new Class[0]; - } - - for (int i = 0; i < vmClass.methodTable.length; ++i) { - if (Method.getName(vmClass.methodTable[i]).equals(name) - && match(parameterTypes, - Method.getParameterTypes(vmClass.methodTable[i]))) - { - return new Method(vmClass.methodTable[i]); - } - } - } - return null; - } - public Method getDeclaredMethod(String name, Class ... parameterTypes) throws NoSuchMethodException { if (name.startsWith("<")) { throw new NoSuchMethodException(name); } - Method m = findMethod(vmClass, name, parameterTypes); - if (m == null) { + int index = Classes.findMethod(vmClass, name, parameterTypes); + if (index < 0) { throw new NoSuchMethodException(name); } else { - return m; + return new Method(vmClass.methodTable[index]); } } @@ -298,9 +220,9 @@ public final class Class implements Type, AnnotatedElement { throw new NoSuchMethodException(name); } for (VMClass c = vmClass; c != null; c = c.super_) { - Method m = findMethod(c, name, parameterTypes); - if (m != null) { - return m; + int index = Classes.findMethod(c, name, parameterTypes); + if (index >= 0) { + return new Method(vmClass.methodTable[index]); } } throw new NoSuchMethodException(name); @@ -309,11 +231,11 @@ public final class Class implements Type, AnnotatedElement { public Constructor getConstructor(Class ... parameterTypes) throws NoSuchMethodException { - Method m = findMethod(vmClass, "", parameterTypes); - if (m == null) { + int index = Classes.findMethod(vmClass, "", parameterTypes); + if (index < 0) { throw new NoSuchMethodException(); } else { - return new Constructor(m); + return new Constructor(new Method(vmClass.methodTable[index])); } } @@ -324,7 +246,7 @@ public final class Class implements Type, AnnotatedElement { Constructor[] constructors = getDeclaredConstructors(); for (int i = 0; i < constructors.length; ++i) { - if (match(parameterTypes, constructors[i].getParameterTypes())) { + if (Classes.match(parameterTypes, constructors[i].getParameterTypes())) { c = constructors[i]; } } @@ -555,6 +477,10 @@ public final class Class implements Type, AnnotatedElement { return (vmClass.vmFlags & PrimitiveFlag) != 0; } + public boolean isEnum() { + return getSuperclass() == Enum.class && (vmClass.flags & EnumFlag) != 0; + } + public URL getResource(String path) { if (! path.startsWith("/")) { String name = new String @@ -626,7 +552,7 @@ public final class Class implements Type, AnnotatedElement { for (VMClass c = vmClass; c != null; c = c.super_) { if (c.addendum != null && c.addendum.annotationTable != null) { Classes.link(c, c.loader); - + Object[] table = (Object[]) c.addendum.annotationTable; for (int i = 0; i < table.length; ++i) { Object[] a = (Object[]) table[i]; diff --git a/classpath/java/lang/Double.java b/classpath/java/lang/Double.java index a58f9fcb39..8d145ce4d9 100644 --- a/classpath/java/lang/Double.java +++ b/classpath/java/lang/Double.java @@ -11,7 +11,7 @@ package java.lang; public final class Double extends Number { - public static final Class TYPE = Class.forCanonicalName("D"); + public static final Class TYPE = avian.Classes.forCanonicalName("D"); public static final double NEGATIVE_INFINITY = -1.0 / 0.0; public static final double POSITIVE_INFINITY = 1.0 / 0.0; diff --git a/classpath/java/lang/Enum.java b/classpath/java/lang/Enum.java index 8d0d4756b0..a70b2074a9 100644 --- a/classpath/java/lang/Enum.java +++ b/classpath/java/lang/Enum.java @@ -30,7 +30,9 @@ public abstract class Enum> implements Comparable { } public static > T valueOf(Class enumType, String name) { - if (name == null) throw new NullPointerException(); + if (name == null) throw new NullPointerException("name"); + if (!enumType.isEnum()) + throw new IllegalArgumentException(enumType.getCanonicalName() + " is not an enum."); try { Method method = enumType.getMethod("values"); @@ -41,10 +43,11 @@ public abstract class Enum> implements Comparable { } } } catch (Exception ex) { - throw new RuntimeException(ex); + // Cannot happen + throw new Error(ex); } - throw new IllegalArgumentException(name); + throw new IllegalArgumentException(enumType.getCanonicalName() + "." + name + " is not an enum constant."); } public int ordinal() { diff --git a/classpath/java/lang/Float.java b/classpath/java/lang/Float.java index f04e728bac..652659f307 100644 --- a/classpath/java/lang/Float.java +++ b/classpath/java/lang/Float.java @@ -11,7 +11,7 @@ package java.lang; public final class Float extends Number { - public static final Class TYPE = Class.forCanonicalName("F"); + public static final Class TYPE = avian.Classes.forCanonicalName("F"); private static final int EXP_BIT_MASK = 0x7F800000; private static final int SIGNIF_BIT_MASK = 0x007FFFFF; diff --git a/classpath/java/lang/Integer.java b/classpath/java/lang/Integer.java index e524cc43c4..f5e4052904 100644 --- a/classpath/java/lang/Integer.java +++ b/classpath/java/lang/Integer.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2013, 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,7 @@ package java.lang; public final class Integer extends Number implements Comparable { - public static final Class TYPE = Class.forCanonicalName("I"); + public static final Class TYPE = avian.Classes.forCanonicalName("I"); public static final int MIN_VALUE = 0x80000000; public static final int MAX_VALUE = 0x7FFFFFFF; @@ -62,6 +62,10 @@ public final class Integer extends Number implements Comparable { return Long.toString(((long) v) & 0xFFFFFFFFL, 16); } + public static String toOctalString(int v) { + return Long.toString(((long) v) & 0xFFFFFFFFL, 8); + } + public static String toBinaryString(int v) { return Long.toString(((long) v) & 0xFFFFFFFFL, 2); } @@ -90,6 +94,27 @@ public final class Integer extends Number implements Comparable { return (double) value; } + public static int signum(int v) { + if (v == 0) return 0; + else if (v > 0) return 1; + else return -1; + } + + // See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + public static int bitCount(int v) { + v = v - ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; + } + + public static int reverseBytes(int v) { + int byte3 = v >>> 24; + int byte2 = (v >>> 8) & 0xFF00; + int byte1 = (v << 8) & 0xFF00; + int byte0 = v << 24; + return (byte0 | byte1 | byte2 | byte3); + } + public static int parseInt(String s) { return parseInt(s, 10); } diff --git a/classpath/java/lang/Long.java b/classpath/java/lang/Long.java index 033ca8b111..3023ad03de 100644 --- a/classpath/java/lang/Long.java +++ b/classpath/java/lang/Long.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2010, Avian Contributors +/* Copyright (c) 2008-2013, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -14,7 +14,7 @@ public final class Long extends Number implements Comparable { public static final long MIN_VALUE = -9223372036854775808l; public static final long MAX_VALUE = 9223372036854775807l; - public static final Class TYPE = Class.forCanonicalName("J"); + public static final Class TYPE = avian.Classes.forCanonicalName("J"); private final long value; @@ -94,6 +94,14 @@ public final class Long extends Number implements Comparable { return toString(v, 16); } + public static String toOctalString(long v) { + return toString(v, 8); + } + + public static String toBinaryString(long v) { + return toString(v, 2); + } + public byte byteValue() { return (byte) value; } @@ -118,6 +126,12 @@ public final class Long extends Number implements Comparable { return (double) value; } + public static int signum(long v) { + if (v == 0) return 0; + else if (v > 0) return 1; + else return -1; + } + private static long pow(long a, long b) { long c = 1; for (int i = 0; i < b; ++i) c *= a; diff --git a/classpath/java/lang/Math.java b/classpath/java/lang/Math.java index 44eea9db17..5f4933244d 100644 --- a/classpath/java/lang/Math.java +++ b/classpath/java/lang/Math.java @@ -93,6 +93,12 @@ public final class Math { public static native double tan(double v); + public static native double cosh(double v); + + public static native double sinh(double v); + + public static native double tanh(double v); + public static native double acos(double v); public static native double asin(double v); diff --git a/classpath/java/lang/Package.java b/classpath/java/lang/Package.java index ed1e4e2985..0b4be0d78f 100644 --- a/classpath/java/lang/Package.java +++ b/classpath/java/lang/Package.java @@ -17,34 +17,66 @@ public class Package { private final String implementationTitle; private final String implementationVendor; private final String implementationVersion; - private final String specementationTitle; - private final String specementationVendor; - private final String specementationVersion; + private final String specificationTitle; + private final String specificationVendor; + private final String specificationVersion; private final URL sealed; private final ClassLoader loader; Package(String name, - String implementationTitle, - String implementationVendor, - String implementationVersion, - String specementationTitle, - String specementationVendor, - String specementationVersion, - URL sealed, + String implementationTitle, + String implementationVendor, + String implementationVersion, + String specificationTitle, + String specificationVendor, + String specificationVersion, + URL sealed, ClassLoader loader) { - this.name = name; - this.implementationTitle = implementationTitle; - this.implementationVendor = implementationVendor; + this.name = name; + this.implementationTitle = implementationTitle; + this.implementationVendor = implementationVendor; this.implementationVersion = implementationVersion; - this.specementationTitle = specementationTitle; - this.specementationVendor = specementationVendor; - this.specementationVersion = specementationVersion; - this.sealed = sealed; - this.loader = loader; + this.specificationTitle = specificationTitle; + this.specificationVendor = specificationVendor; + this.specificationVersion = specificationVersion; + this.sealed = sealed; + this.loader = loader; } public String getName() { return name; } + + public String getImplementationTitle() { + return implementationTitle; + } + + public String getImplementationVendor() { + return implementationVendor; + } + + public String getImplementationVersion() { + return implementationVersion; + } + + public String getSpecificationTitle() { + return specificationTitle; + } + + public String getSpecificationVendor() { + return specificationVendor; + } + + public String getSpecificationVersion() { + return specificationVersion; + } + + public boolean isSealed() { + return sealed != null; + } + + public boolean isSealed(URL url) { + return sealed.equals(url); + } } diff --git a/classpath/java/lang/Short.java b/classpath/java/lang/Short.java index f218bb8cec..2158c8ebec 100644 --- a/classpath/java/lang/Short.java +++ b/classpath/java/lang/Short.java @@ -11,7 +11,7 @@ package java.lang; public final class Short extends Number implements Comparable { - public static final Class TYPE = Class.forCanonicalName("S"); + public static final Class TYPE = avian.Classes.forCanonicalName("S"); public static final short MAX_VALUE = 32767; private final short value; diff --git a/classpath/java/lang/String.java b/classpath/java/lang/String.java index 24a893ad87..ff91fdbc78 100644 --- a/classpath/java/lang/String.java +++ b/classpath/java/lang/String.java @@ -127,6 +127,11 @@ public final class String } else { c = Utf8.decode((byte[])data, offset, length); if(c instanceof char[]) length = ((char[])c).length; + if (c == null) { + throw new RuntimeException + ("unable to parse \"" + new String(data, offset, length, false) + + "\""); + } } this.data = c; diff --git a/classpath/java/lang/Thread.java b/classpath/java/lang/Thread.java index 936351c809..3abfd3238e 100644 --- a/classpath/java/lang/Thread.java +++ b/classpath/java/lang/Thread.java @@ -151,8 +151,8 @@ public class Thread implements Runnable { private static native boolean interrupted(long peer); - public static boolean isInterrupted() { - return currentThread().interrupted; + public boolean isInterrupted() { + return interrupted; } public static void sleep(long milliseconds) throws InterruptedException { diff --git a/classpath/java/lang/Void.java b/classpath/java/lang/Void.java index 89137eea57..55e28e2abf 100644 --- a/classpath/java/lang/Void.java +++ b/classpath/java/lang/Void.java @@ -11,7 +11,7 @@ package java.lang; public final class Void { - public static final Class TYPE = Class.forCanonicalName("V"); + public static final Class TYPE = avian.Classes.forCanonicalName("V"); private Void() { } } diff --git a/classpath/java/lang/reflect/Field.java b/classpath/java/lang/reflect/Field.java index 8d76a6258f..f366341534 100644 --- a/classpath/java/lang/reflect/Field.java +++ b/classpath/java/lang/reflect/Field.java @@ -61,7 +61,7 @@ public class Field extends AccessibleObject { } public Class getType() { - return Class.forCanonicalName + return Classes.forCanonicalName (vmField.class_.loader, new String(vmField.spec, 0, vmField.spec.length - 1, false)); } diff --git a/classpath/java/lang/reflect/InvocationTargetException.java b/classpath/java/lang/reflect/InvocationTargetException.java index abac51cb43..362bfb0788 100644 --- a/classpath/java/lang/reflect/InvocationTargetException.java +++ b/classpath/java/lang/reflect/InvocationTargetException.java @@ -11,6 +11,8 @@ package java.lang.reflect; public class InvocationTargetException extends Exception { + private Throwable target; // for compatibility with OpenJDK + public InvocationTargetException(Throwable targetException, String message) { super(message, targetException); } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index f90ed311a5..f06edf4c03 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -13,6 +13,7 @@ package java.lang.reflect; import avian.VMMethod; import avian.AnnotationInvocationHandler; import avian.SystemClassLoader; +import avian.Classes; import java.lang.annotation.Annotation; @@ -58,61 +59,8 @@ public class Method extends AccessibleObject implements Member { return new String(vmMethod.spec, 0, vmMethod.spec.length - 1, false); } - private static int next(char c, String s, int start) { - for (int i = start; i < s.length(); ++i) { - if (s.charAt(i) == c) return i; - } - throw new RuntimeException(); - } - public Class[] getParameterTypes() { - return getParameterTypes(vmMethod); - } - - public static Class[] getParameterTypes(VMMethod vmMethod) { - int count = vmMethod.parameterCount; - - Class[] types = new Class[count]; - int index = 0; - - String spec = new String - (vmMethod.spec, 1, vmMethod.spec.length - 1, false); - - try { - for (int i = 0; i < spec.length(); ++i) { - char c = spec.charAt(i); - if (c == ')') { - break; - } else if (c == 'L') { - int start = i + 1; - i = next(';', spec, start); - String name = spec.substring(start, i).replace('/', '.'); - types[index++] = Class.forName(name, true, vmMethod.class_.loader); - } else if (c == '[') { - int start = i; - while (spec.charAt(i) == '[') ++i; - - if (spec.charAt(i) == 'L') { - i = next(';', spec, i + 1); - String name = spec.substring(start, i).replace('/', '.'); - types[index++] = Class.forName - (name, true, vmMethod.class_.loader); - } else { - String name = spec.substring(start, i + 1); - types[index++] = Class.forCanonicalName - (vmMethod.class_.loader, name); - } - } else { - String name = spec.substring(i, i + 1); - types[index++] = Class.forCanonicalName - (vmMethod.class_.loader, name); - } - } - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - - return types; + return Classes.getParameterTypes(vmMethod); } public Object invoke(Object instance, Object ... arguments) @@ -151,7 +99,7 @@ public class Method extends AccessibleObject implements Member { public Class getReturnType() { for (int i = 0; i < vmMethod.spec.length - 1; ++i) { if (vmMethod.spec[i] == ')') { - return Class.forCanonicalName + return Classes.forCanonicalName (vmMethod.class_.loader, new String (vmMethod.spec, i + 1, vmMethod.spec.length - i - 2, false)); @@ -160,22 +108,13 @@ public class Method extends AccessibleObject implements Member { throw new RuntimeException(); } - private Annotation getAnnotation(Object[] a) { - if (a[0] == null) { - a[0] = Proxy.newProxyInstance - (vmMethod.class_.loader, new Class[] { (Class) a[1] }, - new AnnotationInvocationHandler(a)); - } - return (Annotation) a[0]; - } - public T getAnnotation(Class class_) { if (vmMethod.hasAnnotations()) { Object[] table = (Object[]) vmMethod.addendum.annotationTable; for (int i = 0; i < table.length; ++i) { Object[] a = (Object[]) table[i]; if (a[1] == class_) { - return (T) getAnnotation(a); + return (T) Classes.getAnnotation(vmMethod.class_.loader, a); } } } @@ -187,7 +126,8 @@ public class Method extends AccessibleObject implements Member { Object[] table = (Object[]) vmMethod.addendum.annotationTable; Annotation[] array = new Annotation[table.length]; for (int i = 0; i < table.length; ++i) { - array[i] = getAnnotation((Object[]) table[i]); + array[i] = Classes.getAnnotation + (vmMethod.class_.loader, (Object[]) table[i]); } return array; } else { diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java index cfc35a6077..f3db1434b5 100644 --- a/classpath/java/lang/reflect/Proxy.java +++ b/classpath/java/lang/reflect/Proxy.java @@ -16,6 +16,9 @@ import static avian.Stream.write4; import static avian.Stream.set4; import static avian.Assembler.*; +import avian.SystemClassLoader; +import avian.Classes; + import avian.ConstantPool; import avian.ConstantPool.PoolEntry; @@ -87,26 +90,15 @@ public class Proxy { write1(out, aload_0); - write1(out, new_); - write2(out, ConstantPool.addClass(pool, "java/lang/reflect/Method") + 1); - write1(out, dup); write1(out, ldc_w); write2(out, ConstantPool.addClass(pool, className) + 1); - write1(out, getfield); - write2(out, ConstantPool.addFieldRef - (pool, "java/lang/Class", - "vmClass", "Lavian/VMClass;") + 1); - write1(out, getfield); - write2(out, ConstantPool.addFieldRef - (pool, "avian/VMClass", - "methodTable", "[Lavian/VMMethod;") + 1); write1(out, ldc_w); write2(out, ConstantPool.addInteger(pool, index) + 1); - write1(out, aaload); - write1(out, invokespecial); + write1(out, invokestatic); write2(out, ConstantPool.addMethodRef - (pool, "java/lang/reflect/Method", - "", "(Lavian/VMMethod;)V") + 1); + (pool, "avian/Classes", + "makeMethod", "(Ljava/lang/Class;I)Ljava/lang/reflect/Method;") + + 1); write1(out, ldc_w); write2(out, ConstantPool.addInteger(pool, parameterCount) + 1); @@ -363,10 +355,11 @@ public class Proxy { Map virtualMap = new HashMap(); for (Class c: interfaces) { - avian.VMMethod[] ivtable = c.vmClass.virtualTable; + avian.VMMethod[] ivtable = SystemClassLoader.vmClass(c).virtualTable; if (ivtable != null) { for (avian.VMMethod m: ivtable) { - virtualMap.put(Method.getName(m) + Method.getSpec(m), m); + virtualMap.put + (Classes.toString(m.name) + Classes.toString(m.spec), m); } } } @@ -376,15 +369,15 @@ public class Proxy { for (avian.VMMethod m: virtualMap.values()) { methodTable[i] = new MethodData (0, - ConstantPool.addUtf8(pool, Method.getName(m)), - ConstantPool.addUtf8(pool, Method.getSpec(m)), + ConstantPool.addUtf8(pool, Classes.toString(m.name)), + ConstantPool.addUtf8(pool, Classes.toString(m.spec)), makeInvokeCode(pool, name, m.spec, m.parameterCount, m.parameterFootprint, i)); ++ i; } methodTable[i++] = new MethodData - (0, + (Modifier.PUBLIC, ConstantPool.addUtf8(pool, ""), ConstantPool.addUtf8 (pool, "(Ljava/lang/reflect/InvocationHandler;)V"), diff --git a/classpath/java/math/BigInteger.java b/classpath/java/math/BigInteger.java new file mode 100644 index 0000000000..319b01707b --- /dev/null +++ b/classpath/java/math/BigInteger.java @@ -0,0 +1,44 @@ +/* Copyright (c) 2013, 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.math; + +import java.io.Serializable; + +public class BigInteger implements Serializable { + + private int sign; + private int[] value; + + private BigInteger(int sign, long value) { + this.sign = sign; + int upperBits = (int) (value >>> 32); + if (upperBits == 0) + // Array with one element + this.value = new int[] { (int) value }; + else + // Array with two elements + this.value = new int[] { (int) value, upperBits }; + } + + public static final BigInteger ZERO = new BigInteger(0, 0); + public static final BigInteger ONE = new BigInteger(1, 1); + public static final BigInteger TEN = new BigInteger(1, 10); + + public static BigInteger valueOf(long num) { + int signum = Long.signum(num); + if (signum == 0) + return BigInteger.ZERO; + else if (signum > 0) + return new BigInteger(signum, num); + else + return new BigInteger(signum, -num); + } +} diff --git a/classpath/java/math/MathContext.java b/classpath/java/math/MathContext.java new file mode 100644 index 0000000000..7ff24d8b12 --- /dev/null +++ b/classpath/java/math/MathContext.java @@ -0,0 +1,71 @@ +/* Copyright (c) 2013, 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.math; + +import java.io.Serializable; + +public final class MathContext implements Serializable { + + public static final MathContext DECIMAL32 = new MathContext( 7, RoundingMode.HALF_EVEN); + public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN); + public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN); + public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP); + + private int precision; + private RoundingMode roundingMode; + + public MathContext(int precision, RoundingMode roundingMode) { + if (precision < 0) + throw new IllegalArgumentException(); + if (roundingMode == null) + throw new NullPointerException(); + this.precision = precision; + this.roundingMode = roundingMode; + } + + public MathContext(int precision) { + this(precision, RoundingMode.HALF_UP); + } + + public int getPrecision() { + return precision; + } + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + @Override + public boolean equals(Object that) { + return + (that instanceof MathContext) && + (precision == ((MathContext) that).getPrecision()) && + (roundingMode == ((MathContext) that).getRoundingMode()); + } + + @Override + public int hashCode() { + return + roundingMode.ordinal() | + (precision << 4); + } + + private final static String precisionString = "precision="; + private final static String roundingModeString = " roundingMode="; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(48); + sb.append(precisionString).append(precision); + sb.append(roundingModeString).append(roundingMode); + return sb.toString(); + } +} diff --git a/classpath/java/math/RoundingMode.java b/classpath/java/math/RoundingMode.java new file mode 100644 index 0000000000..22bfd69757 --- /dev/null +++ b/classpath/java/math/RoundingMode.java @@ -0,0 +1,36 @@ +/* Copyright (c) 2013, 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.math; + +public enum RoundingMode { + + UP (0), + DOWN (1), + CEILING (2), + FLOOR (3), + HALF_UP (4), + HALF_DOWN (5), + HALF_EVEN (6), + UNNECESSARY(7); + + RoundingMode(int rm) { + roundingMode = rm; + } + + private final int roundingMode; + + public static RoundingMode valueOf(int roundingMode) { + final RoundingMode[] values = values(); + if (roundingMode < 0 || roundingMode >= values.length) + throw new IllegalArgumentException(); + return values[roundingMode]; + } +} diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index 3c2852f394..c311ad8c15 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -83,8 +83,8 @@ public final class URL { { if ("http".equals(protocol) || "https".equals(protocol)) { return new avian.http.Handler(); - } else if ("avian_vm_resource".equals(protocol)) { - return new avian.avian_vm_resource.Handler(); + } else if ("avianvmresource".equals(protocol)) { + return new avian.avianvmresource.Handler(); } else if ("file".equals(protocol)) { return new avian.file.Handler(); } else if ("jar".equals(protocol)) { diff --git a/classpath/java/net/URLClassLoader.java b/classpath/java/net/URLClassLoader.java new file mode 100644 index 0000000000..d4233cfd42 --- /dev/null +++ b/classpath/java/net/URLClassLoader.java @@ -0,0 +1,57 @@ +package java.net; + +import java.io.File; + +import java.io.InputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +public class URLClassLoader extends ClassLoader { + + private final File jarFile; + + public URLClassLoader(URL[] urls, ClassLoader parent) { + super(parent); + if(urls.length != 1) { + throw new UnsupportedOperationException(); + } + if(!urls[0].getProtocol().equals("file")) { + throw new UnsupportedOperationException(urls[0].getProtocol()); + } + this.jarFile = new File(urls[0].getFile()); + } + + + protected Class findClass(String name) throws ClassNotFoundException { + try { + InputStream stream = getResourceAsStream(name.replace(".", "/") + ".class"); + if(stream == null) { + throw new ClassNotFoundException("couldn't find class " + name); + } + byte[] buf = new byte[2048]; + ByteArrayOutputStream mem = new ByteArrayOutputStream(); + try { + int size; + while((size = stream.read(buf, 0, buf.length)) > 0) { + mem.write(buf, 0, size); + } + byte[] data = mem.toByteArray(); + return defineClass(name, data, 0, data.length); + } finally { + stream.close(); + } + } catch(IOException e) { + throw new ClassNotFoundException("couldn't find class " + name, e); + } + } + + public URL getResource(String path) { + try { + return new URL("jar:file:" + jarFile.getAbsolutePath() + "!/" + path); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } + } + + +} \ No newline at end of file diff --git a/classpath/java/nio/ArrayByteBuffer.java b/classpath/java/nio/ArrayByteBuffer.java index 2f2e7d5024..336945c1bb 100644 --- a/classpath/java/nio/ArrayByteBuffer.java +++ b/classpath/java/nio/ArrayByteBuffer.java @@ -31,6 +31,10 @@ class ArrayByteBuffer extends ByteBuffer { return b; } + public boolean hasArray() { + return true; + } + public byte[] array() { return array; } @@ -49,9 +53,10 @@ class ArrayByteBuffer extends ByteBuffer { } public ByteBuffer put(ByteBuffer src) { - checkPut(position, src.remaining()); - src.get(array, arrayOffset + position, src.remaining()); - position += src.remaining(); + int length = src.remaining(); + checkPut(position, length); + src.get(array, arrayOffset + position, length); + position += length; return this; } diff --git a/classpath/java/nio/ByteBuffer.java b/classpath/java/nio/ByteBuffer.java index 47eec8c1dc..3a6548d2b3 100644 --- a/classpath/java/nio/ByteBuffer.java +++ b/classpath/java/nio/ByteBuffer.java @@ -53,13 +53,15 @@ public abstract class ByteBuffer } public ByteBuffer compact() { + int remaining = remaining(); + if (position != 0) { ByteBuffer b = slice(); position = 0; put(b); } - position = remaining(); + position = remaining; limit(capacity()); return this; diff --git a/classpath/java/nio/ByteOrder.java b/classpath/java/nio/ByteOrder.java new file mode 100644 index 0000000000..8e2b432ed2 --- /dev/null +++ b/classpath/java/nio/ByteOrder.java @@ -0,0 +1,40 @@ +/* Copyright (c) 2012, 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.nio; + +public final class ByteOrder { + public static final ByteOrder BIG_ENDIAN = new ByteOrder("BIG_ENDIAN"); + public static final ByteOrder LITTLE_ENDIAN = new ByteOrder("LITTLE_ENDIAN"); + + private static final ByteOrder NATIVE; + private static native boolean isNativeBigEndian(); + + static { + if (isNativeBigEndian()) + NATIVE = BIG_ENDIAN; + else + NATIVE = LITTLE_ENDIAN; + } + + private String name; + + private ByteOrder(String name) { + this.name = name; + } + + public String toString() { + return name; + } + + public static ByteOrder nativeOrder() { + return NATIVE; + } +} diff --git a/classpath/java/util/jar/Attributes.java b/classpath/java/util/jar/Attributes.java new file mode 100644 index 0000000000..806fdb5b98 --- /dev/null +++ b/classpath/java/util/jar/Attributes.java @@ -0,0 +1,27 @@ +/* Copyright (c) 2013, 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.jar; + +public class Attributes { + public static class Name { + private final String name; + + private static final int MAX_NAME_LENGTH = 70; + + public Name(String s) { + int len = s.length(); + if (len == 0 || len > MAX_NAME_LENGTH) + throw new IllegalArgumentException(); + + name = s; + } + } +} diff --git a/classpath/jni-util.h b/classpath/jni-util.h index 57e545a14b..bed4658953 100644 --- a/classpath/jni-util.h +++ b/classpath/jni-util.h @@ -15,6 +15,8 @@ #include "stdlib.h" #include "string.h" +#include + #undef JNIEXPORT #if (defined __MINGW32__) || (defined _MSC_VER) @@ -121,30 +123,5 @@ allocate(JNIEnv* e, unsigned size) } return p; } -#ifdef _MSC_VER - -template -class RuntimeArray { - public: - RuntimeArray(unsigned size): - body(static_cast(malloc(size * sizeof(T)))) - { } - - ~RuntimeArray() { - free(body); - } - - T* body; -}; - -# define RUNTIME_ARRAY(type, name, size) RuntimeArray name(size); -# define RUNTIME_ARRAY_BODY(name) name.body - -#else // not _MSC_VER - -# define RUNTIME_ARRAY(type, name, size) type name[size]; -# define RUNTIME_ARRAY_BODY(name) name - -#endif // not _MSC_VER #endif//JNI_UTIL diff --git a/classpath/sun/misc/Unsafe.java b/classpath/sun/misc/Unsafe.java index 4f395718c9..5fea56c54a 100644 --- a/classpath/sun/misc/Unsafe.java +++ b/classpath/sun/misc/Unsafe.java @@ -1,5 +1,7 @@ package sun.misc; +import java.lang.reflect.Field; + public final class Unsafe { private void Unsafe() { } @@ -50,10 +52,15 @@ public final class Unsafe { public native int arrayBaseOffset(Class arrayClass); + public native long objectFieldOffset(Field field); + public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long count); + public native boolean compareAndSwapInt(Object o, long offset, int old, + int new_); + public void copyMemory(long src, long dst, long count) { copyMemory(null, src, null, dst, count); } diff --git a/src/binaryToObject/tools.h b/include/avian/tools/object-writer/tools.h similarity index 91% rename from src/binaryToObject/tools.h rename to include/avian/tools/object-writer/tools.h index 46bcd691e8..b9ed905e62 100644 --- a/src/binaryToObject/tools.h +++ b/include/avian/tools/object-writer/tools.h @@ -12,7 +12,10 @@ #define AVIAN_TOOLS_H_ #include -#include "environment.h" + +#include + +#include "avian/environment.h" namespace avian { @@ -38,24 +41,12 @@ public: virtual void write(uint8_t byte); }; -class String { -public: - const char* text; - size_t length; - - String(const char* text); - - inline String(const char* text, size_t length): - text(text), - length(length) {} -}; - class SymbolInfo { public: unsigned addr; - String name; + util::String name; - inline SymbolInfo(uint64_t addr, const String& name): + inline SymbolInfo(uint64_t addr, const util::String& name): addr(addr), name(name) {} @@ -78,7 +69,7 @@ public: class StringTable : public Buffer { public: - unsigned add(String str); + unsigned add(util::String str); }; template diff --git a/include/avian/util/abort.h b/include/avian/util/abort.h new file mode 100644 index 0000000000..d209479b97 --- /dev/null +++ b/include/avian/util/abort.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_UTIL_ABORT_H +#define AVIAN_UTIL_ABORT_H + +namespace avian { +namespace util { + +class Aborter { +public: + virtual void NO_RETURN abort() = 0; +}; + +template +inline void NO_RETURN abort(T t) { + getAborter(t)->abort(); + ::abort(); +} + +template +inline void expect(T t, bool v) { + if(UNLIKELY(!v)) { + abort(t); + } +} + +#ifdef NDEBUG +#define assert(t, v) +#else +template +inline void assert(T t, bool v) { + expect(t, v); +} +#endif + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_ABORT_H diff --git a/include/avian/util/arg-parser.h b/include/avian/util/arg-parser.h new file mode 100644 index 0000000000..1f887764f2 --- /dev/null +++ b/include/avian/util/arg-parser.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2008-2011, 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. */ + +#ifndef AVIAN_UTIL_ARG_PARSER_H +#define AVIAN_UTIL_ARG_PARSER_H + +namespace avian { +namespace util { + +class Arg; + +class ArgParser { +public: + Arg* first; + Arg** last; + + ArgParser(); + + bool parse(int ac, const char* const* av); + void printUsage(const char* exe); +}; + +class Arg { +public: + Arg* next; + bool required; + const char* name; + const char* desc; + + const char* value; + + Arg(ArgParser& parser, bool required, const char* name, const char* desc); +}; + + +} // namespace avian +} // namespace util + +#endif // AVIAN_UTIL_ARG_PARSER_H \ No newline at end of file diff --git a/include/avian/util/math.h b/include/avian/util/math.h new file mode 100644 index 0000000000..25337abb47 --- /dev/null +++ b/include/avian/util/math.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_UTIL_MATH_H +#define AVIAN_UTIL_MATH_H + +#undef max +#undef min + +namespace avian { +namespace util { + +inline unsigned max(unsigned a, unsigned b) { + return (a > b ? a : b); +} + +inline unsigned min(unsigned a, unsigned b) { + return (a < b ? a : b); +} + +inline unsigned avg(unsigned a, unsigned b) { + return (a + b) / 2; +} + +inline unsigned ceilingDivide(unsigned n, unsigned d) { + return (n + d - 1) / d; +} + +inline bool powerOfTwo(unsigned n) { + for (; n > 2; n >>= 1) if (n & 1) return false; + return true; +} + +inline unsigned nextPowerOfTwo(unsigned n) { + unsigned r = 1; + while (r < n) r <<= 1; + return r; +} + +inline unsigned log(unsigned n) { + unsigned r = 0; + for (unsigned i = 1; i < n; ++r) i <<= 1; + return r; +} + +} // namespace util +} // namespace avian + +#endif // AVIAN_UTIL_MATH_H diff --git a/include/avian/util/runtime-array.h b/include/avian/util/runtime-array.h new file mode 100644 index 0000000000..5d169ed95e --- /dev/null +++ b/include/avian/util/runtime-array.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_UTIL_RUNTIME_ARRAY_H +#define AVIAN_UTIL_RUNTIME_ARRAY_H + +#ifdef _MSC_VER + +template +class RuntimeArray { + public: + RuntimeArray(unsigned size): + body(static_cast(malloc(size * sizeof(T)))) + { } + + ~RuntimeArray() { + free(body); + } + + T* body; +}; + +# define RUNTIME_ARRAY(type, name, size) RuntimeArray name(size); +# define RUNTIME_ARRAY_BODY(name) name.body + +#else // not _MSC_VER + +# define RUNTIME_ARRAY(type, name, size) type name##_body[size]; +# define RUNTIME_ARRAY_BODY(name) name##_body + +#endif + +#endif // AVIAN_UTIL_RUNTIME_ARRAY_H diff --git a/src/stream.h b/include/avian/util/stream.h similarity index 73% rename from src/stream.h rename to include/avian/util/stream.h index a08bf3b1a0..f3dbed082c 100644 --- a/src/stream.h +++ b/include/avian/util/stream.h @@ -11,19 +11,19 @@ #ifndef STREAM_H #define STREAM_H -#include "common.h" +#include "avian/common.h" namespace vm { -class Stream { +class AbstractStream { public: class Client { public: virtual void handleError() = 0; }; - Stream(Client* client, const uint8_t* data, unsigned size): - client(client), data(data), size(size), position_(0) + AbstractStream(Client* client, unsigned size): + client(client), size(size), position_(0) { } unsigned position() { @@ -42,13 +42,13 @@ class Stream { } } - void read(uint8_t* data, unsigned size) { + void read(uint8_t* dst, unsigned size) { if (size > this->size - position_) { - memset(data, 0, size); + memset(dst, 0, size); client->handleError(); } else { - memcpy(data, this->data + position_, size); + copy(dst, position_, size); position_ += size; } } @@ -85,13 +85,29 @@ class Stream { return read8(); } + protected: + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) = 0; + private: Client* client; - const uint8_t* data; unsigned size; unsigned position_; }; +class Stream: public AbstractStream { + public: + Stream(Client* client, const uint8_t* data, unsigned size): + AbstractStream(client, size), data(data) + { } + + private: + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) { + memcpy(dst, data + offset, size); + } + + const uint8_t* data; +}; + } // namespace vm #endif//STREAM_H diff --git a/include/avian/util/string.h b/include/avian/util/string.h new file mode 100644 index 0000000000..b0b338abfa --- /dev/null +++ b/include/avian/util/string.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2010-2011, 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. */ + +#ifndef AVIAN_UTIL_STRING_H +#define AVIAN_UTIL_STRING_H + +#include + +namespace avian { +namespace util { + +class String { +public: + const char* text; + size_t length; + + String(const char* text): + text(text), + length(strlen(text)) {} + + inline String(const char* text, size_t length): + text(text), + length(length) {} +}; + +class Tokenizer { + public: + + Tokenizer(const char* s, char delimiter): + s(s), limit(0), delimiter(delimiter) + { } + + Tokenizer(String str, char delimiter): + s(str.text), limit(str.text + str.length), delimiter(delimiter) + { } + + bool hasMore() { + while (s != limit and *s == delimiter) ++s; + return s != limit and *s != 0; + } + + String next() { + const char* p = s; + while (s != limit and *s and *s != delimiter) ++s; + return String(p, s - p); + } + + const char* s; + const char* limit; + char delimiter; +}; + +} // namespace util +} // namespace avain + +#endif//AVIAN_UTIL_STRING_H diff --git a/include/avian/vm/codegen/architecture.h b/include/avian/vm/codegen/architecture.h new file mode 100644 index 0000000000..07c3fc5e35 --- /dev/null +++ b/include/avian/vm/codegen/architecture.h @@ -0,0 +1,137 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ARCHITECTURE_H +#define AVIAN_CODEGEN_ARCHITECTURE_H + +namespace vm { +class Allocator; +class Zone; +} + +namespace avian { +namespace codegen { + +class Assembler; + +class RegisterFile; + +class OperandMask { +public: + uint8_t typeMask; + uint64_t registerMask; + + OperandMask(uint8_t typeMask, uint64_t registerMask): + typeMask(typeMask), + registerMask(registerMask) + { } + + OperandMask(): + typeMask(~0), + registerMask(~static_cast(0)) + { } +}; + +class Architecture { +public: +virtual unsigned floatRegisterSize() = 0; + +virtual const RegisterFile* registerFile() = 0; + +virtual int scratch() = 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 uintptr_t maximumImmediateJump() = 0; + +virtual bool alwaysCondensed(lir::BinaryOperation op) = 0; +virtual bool alwaysCondensed(lir::TernaryOperation op) = 0; + +virtual bool reserved(int register_) = 0; + +virtual unsigned frameFootprint(unsigned footprint) = 0; +virtual unsigned argumentFootprint(unsigned footprint) = 0; +virtual bool argumentAlignment() = 0; +virtual bool argumentRegisterAlignment() = 0; +virtual unsigned argumentRegisterCount() = 0; +virtual int argumentRegister(unsigned index) = 0; + +virtual bool hasLinkRegister() = 0; + +virtual unsigned stackAlignmentInWords() = 0; + +virtual bool matchCall(void* returnAddress, void* target) = 0; + +virtual void updateCall(lir::UnaryOperation op, void* returnAddress, + void* newTarget) = 0; + +virtual void setConstant(void* dst, uint64_t constant) = 0; + +virtual unsigned alignFrameSize(unsigned sizeInWords) = 0; + +virtual void nextFrame(void* start, unsigned size, unsigned footprint, + void* link, bool mostRecent, + unsigned targetParameterFootprint, void** ip, + void** stack) = 0; +virtual void* frameIp(void* stack) = 0; +virtual unsigned frameHeaderSize() = 0; +virtual unsigned frameReturnAddressSize() = 0; +virtual unsigned frameFooterSize() = 0; +virtual int returnAddressOffset() = 0; +virtual int framePointerOffset() = 0; + +virtual void plan +(lir::UnaryOperation op, + unsigned aSize, OperandMask& aMask, + bool* thunk) = 0; + +virtual void planSource +(lir::BinaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned bSize, bool* thunk) = 0; + +virtual void planDestination +(lir::BinaryOperation op, + unsigned aSize, const OperandMask& aMask, + unsigned bSize, OperandMask& bMask) = 0; + +virtual void planMove +(unsigned size, OperandMask& src, + OperandMask& tmp, + const OperandMask& dst) = 0; + +virtual void planSource +(lir::TernaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned bSize, OperandMask& bMask, + unsigned cSize, bool* thunk) = 0; + +virtual void planDestination +(lir::TernaryOperation op, + unsigned aSize, const OperandMask& aMask, + unsigned bSize, const OperandMask& bMask, + unsigned cSize, OperandMask& cMask) = 0; + +virtual Assembler* makeAssembler(vm::Allocator*, vm::Zone*) = 0; + +virtual void acquire() = 0; +virtual void release() = 0; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ARCHITECTURE_H \ No newline at end of file diff --git a/include/avian/vm/codegen/assembler.h b/include/avian/vm/codegen/assembler.h new file mode 100644 index 0000000000..c242a7310e --- /dev/null +++ b/include/avian/vm/codegen/assembler.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_H +#define AVIAN_CODEGEN_ASSEMBLER_H + +#include +#include "avian/zone.h" + +#include +#include + +namespace avian { +namespace codegen { + +class Architecture; + +class OperandInfo { +public: + const unsigned size; + const lir::OperandType type; + lir::Operand* const operand; + + inline OperandInfo(unsigned size, lir::OperandType type, lir::Operand* operand): + size(size), + type(type), + operand(operand) + { } +}; + +#ifdef AVIAN_TAILS +const bool TailCalls = true; +#else +const bool TailCalls = false; +#endif + +#if (defined AVIAN_USE_FRAME_POINTER) || (defined ARCH_powerpc) +const bool UseFramePointer = true; +#else +const bool UseFramePointer = false; +#endif + +class Assembler { + public: + + class Client { + public: + virtual int acquireTemporary + (uint32_t mask = ~static_cast(0)) = 0; + virtual void releaseTemporary(int r) = 0; + + virtual void save(int r) = 0; + }; + + class Block { + public: + virtual unsigned resolve(unsigned start, Block* next) = 0; + }; + + virtual void setClient(Client* client) = 0; + + virtual Architecture* arch() = 0; + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) = 0; + virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) = 0; + virtual void pushFrame(unsigned argumentCount, ...) = 0; + virtual void allocateFrame(unsigned footprint) = 0; + virtual void adjustFrame(unsigned difference) = 0; + virtual void popFrame(unsigned footprint) = 0; + virtual void popFrameForTailCall(unsigned footprint, int offset, + int returnAddressSurrogate, + int framePointerSurrogate) = 0; + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + = 0; + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + = 0; + + virtual void apply(lir::Operation op) = 0; + virtual void apply(lir::UnaryOperation op, OperandInfo a) = 0; + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) = 0; + virtual void apply(lir::TernaryOperation op, OperandInfo a, OperandInfo b, OperandInfo c) = 0; + + virtual void setDestination(uint8_t* dst) = 0; + + virtual void write() = 0; + + virtual Promise* offset(bool forTrace = false) = 0; + + virtual Block* endBlock(bool startNew) = 0; + + virtual void endEvent() = 0; + + virtual unsigned length() = 0; + + virtual unsigned footerSize() = 0; + + virtual void dispose() = 0; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_H diff --git a/src/compiler.h b/include/avian/vm/codegen/compiler.h similarity index 93% rename from src/compiler.h rename to include/avian/vm/codegen/compiler.h index e66c69a67c..49dfa14095 100644 --- a/src/compiler.h +++ b/include/avian/vm/codegen/compiler.h @@ -8,14 +8,15 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#ifndef COMPILER_H -#define COMPILER_H +#ifndef AVIAN_CODEGEN_COMPILER_H +#define AVIAN_CODEGEN_COMPILER_H -#include "system.h" -#include "zone.h" +#include +#include "avian/zone.h" #include "assembler.h" -namespace vm { +namespace avian { +namespace codegen { class TraceHandler { public: @@ -26,10 +27,10 @@ class Compiler { public: class Client { public: - virtual intptr_t getThunk(UnaryOperation op, unsigned size) = 0; - virtual intptr_t getThunk(BinaryOperation op, unsigned size, + virtual intptr_t getThunk(lir::UnaryOperation op, unsigned size) = 0; + virtual intptr_t getThunk(lir::BinaryOperation op, unsigned size, unsigned resultSize) = 0; - virtual intptr_t getThunk(TernaryOperation op, unsigned size, + virtual intptr_t getThunk(lir::TernaryOperation op, unsigned size, unsigned resultSize, bool* threadParameter) = 0; }; @@ -200,9 +201,10 @@ class Compiler { }; Compiler* -makeCompiler(System* system, Assembler* assembler, Zone* zone, +makeCompiler(vm::System* system, Assembler* assembler, vm::Zone* zone, Compiler::Client* client); -} // namespace vm +} // namespace codegen +} // namespace avian -#endif//COMPILER_H +#endif // AVIAN_CODEGEN_COMPILER_H diff --git a/include/avian/vm/codegen/lir-ops.inc.cpp b/include/avian/vm/codegen/lir-ops.inc.cpp new file mode 100644 index 0000000000..cf038c6647 --- /dev/null +++ b/include/avian/vm/codegen/lir-ops.inc.cpp @@ -0,0 +1,62 @@ +LIR_OP_0(Return) +LIR_OP_0(LoadBarrier) +LIR_OP_0(StoreStoreBarrier) +LIR_OP_0(StoreLoadBarrier) +LIR_OP_0(Trap) + +LIR_OP_1(Call) +LIR_OP_1(LongCall) +LIR_OP_1(AlignedLongCall) +LIR_OP_1(AlignedCall) +LIR_OP_1(Jump) +LIR_OP_1(LongJump) +LIR_OP_1(AlignedLongJump) +LIR_OP_1(AlignedJump) + +LIR_OP_2(Move) +LIR_OP_2(MoveLow) +LIR_OP_2(MoveHigh) +LIR_OP_2(MoveZ) +LIR_OP_2(Negate) +LIR_OP_2(FloatNegate) +LIR_OP_2(Float2Float) +LIR_OP_2(Float2Int) +LIR_OP_2(Int2Float) +LIR_OP_2(FloatSquareRoot) +LIR_OP_2(FloatAbsolute) +LIR_OP_2(Absolute) + +LIR_OP_3(Add) +LIR_OP_3(Subtract) +LIR_OP_3(Multiply) +LIR_OP_3(Divide) +LIR_OP_3(Remainder) +LIR_OP_3(ShiftLeft) +LIR_OP_3(ShiftRight) +LIR_OP_3(UnsignedShiftRight) +LIR_OP_3(And) +LIR_OP_3(Or) +LIR_OP_3(Xor) +LIR_OP_3(FloatAdd) +LIR_OP_3(FloatSubtract) +LIR_OP_3(FloatMultiply) +LIR_OP_3(FloatDivide) +LIR_OP_3(FloatRemainder) +LIR_OP_3(FloatMax) +LIR_OP_3(FloatMin) +LIR_OP_3(JumpIfLess) +LIR_OP_3(JumpIfGreater) +LIR_OP_3(JumpIfLessOrEqual) +LIR_OP_3(JumpIfGreaterOrEqual) +LIR_OP_3(JumpIfEqual) +LIR_OP_3(JumpIfNotEqual) +LIR_OP_3(JumpIfFloatEqual) +LIR_OP_3(JumpIfFloatNotEqual) +LIR_OP_3(JumpIfFloatLess) +LIR_OP_3(JumpIfFloatGreater) +LIR_OP_3(JumpIfFloatLessOrEqual) +LIR_OP_3(JumpIfFloatGreaterOrEqual) +LIR_OP_3(JumpIfFloatLessOrUnordered) +LIR_OP_3(JumpIfFloatGreaterOrUnordered) +LIR_OP_3(JumpIfFloatLessOrEqualOrUnordered) +LIR_OP_3(JumpIfFloatGreaterOrEqualOrUnordered) diff --git a/include/avian/vm/codegen/lir.h b/include/avian/vm/codegen/lir.h new file mode 100644 index 0000000000..b95e736f06 --- /dev/null +++ b/include/avian/vm/codegen/lir.h @@ -0,0 +1,174 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_LIR_H +#define AVIAN_CODEGEN_LIR_H + +namespace avian { +namespace codegen { +class Promise; + +namespace lir { +enum Operation { + #define LIR_OP_0(x) x, + #define LIR_OP_1(x) + #define LIR_OP_2(x) + #define LIR_OP_3(x) + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 +}; + +const unsigned OperationCount = Trap + 1; + +enum UnaryOperation { + #define LIR_OP_0(x) + #define LIR_OP_1(x) x, + #define LIR_OP_2(x) + #define LIR_OP_3(x) + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 + + NoUnaryOperation = -1 +}; + +const unsigned UnaryOperationCount = AlignedJump + 1; + +enum BinaryOperation { + #define LIR_OP_0(x) + #define LIR_OP_1(x) + #define LIR_OP_2(x) x, + #define LIR_OP_3(x) + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 + + NoBinaryOperation = -1 +}; + +const unsigned BinaryOperationCount = Absolute + 1; + +enum TernaryOperation { + #define LIR_OP_0(x) + #define LIR_OP_1(x) + #define LIR_OP_2(x) + #define LIR_OP_3(x) x, + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 + + NoTernaryOperation = -1 +}; + +const unsigned TernaryOperationCount += JumpIfFloatGreaterOrEqualOrUnordered + 1; + +const unsigned NonBranchTernaryOperationCount = FloatMin + 1; +const unsigned BranchOperationCount += JumpIfFloatGreaterOrEqualOrUnordered - FloatMin; + +enum OperandType { + ConstantOperand, + AddressOperand, + RegisterOperand, + MemoryOperand +}; + +enum ValueType { + ValueGeneral, + ValueFloat +}; + +const unsigned OperandTypeCount = MemoryOperand + 1; + +const int NoRegister = -1; + + +inline bool isBranch(lir::TernaryOperation op) { + return op > FloatMin; +} + +inline bool isFloatBranch(lir::TernaryOperation op) { + return op > JumpIfNotEqual; +} + +class Operand { }; + +class Constant: public Operand { + public: + Constant(Promise* value): value(value) { } + + Promise* value; +}; + +class Address: public Operand { + public: + Address(Promise* address): address(address) { } + + Promise* address; +}; + +class Register: public Operand { + public: + Register(int low, int high = NoRegister): low(low), high(high) { } + + int low; + int high; +}; + +class Memory: public Operand { + public: + Memory(int base, int offset, int index = NoRegister, unsigned scale = 1): + base(base), offset(offset), index(index), scale(scale) + { } + + int base; + int offset; + int index; + unsigned scale; +}; + +class Instr { +public: + + enum Opcode { + #define LIR_OP_0(x) OP_##x, + #define LIR_OP_1(x) OP_##x, + #define LIR_OP_2(x) OP_##x, + #define LIR_OP_3(x) OP_##x, + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 + }; + + static const char* opcodeName(Opcode op); + + static Opcode opcodeFromNullary(Operation op); + static Opcode opcodeFromUnary(UnaryOperation op); + static Opcode opcodeFromBinary(BinaryOperation op); + static Opcode opcodeFromTernary(TernaryOperation op); +}; + +} // namespace lir +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_LIR_H diff --git a/include/avian/vm/codegen/promise.h b/include/avian/vm/codegen/promise.h new file mode 100644 index 0000000000..a5f0c3db3d --- /dev/null +++ b/include/avian/vm/codegen/promise.h @@ -0,0 +1,159 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_PROMISE_H +#define AVIAN_CODEGEN_PROMISE_H + +#include "avian/allocator.h" + +namespace avian { +namespace codegen { + +class Promise { + public: + class Listener { + public: + virtual bool resolve(int64_t value, void** location) = 0; + + Listener* next; + }; + + virtual int64_t value() = 0; + virtual bool resolved() = 0; + virtual Listener* listen(unsigned) { return 0; } +}; + +class ResolvedPromise: public Promise { + public: + ResolvedPromise(int64_t value): value_(value) { } + + virtual int64_t value() { + return value_; + } + + virtual bool resolved() { + return true; + } + + int64_t value_; +}; + +class ShiftMaskPromise: public Promise { + public: + ShiftMaskPromise(Promise* base, unsigned shift, int64_t mask): + base(base), shift(shift), mask(mask) + { } + + virtual int64_t value() { + return (base->value() >> shift) & mask; + } + + virtual bool resolved() { + return base->resolved(); + } + + Promise* base; + unsigned shift; + int64_t mask; +}; + +class CombinedPromise: public Promise { + public: + CombinedPromise(Promise* low, Promise* high): + low(low), high(high) + { } + + virtual int64_t value() { + return low->value() | (high->value() << 32); + } + + virtual bool resolved() { + return low->resolved() and high->resolved(); + } + + Promise* low; + Promise* high; +}; + +class OffsetPromise: public Promise { + public: + OffsetPromise(Promise* base, int64_t offset): + base(base), offset(offset) + { } + + virtual int64_t value() { + return base->value() + offset; + } + + virtual bool resolved() { + return base->resolved(); + } + + Promise* base; + int64_t offset; +}; + +class ListenPromise: public Promise { + public: + ListenPromise(vm::System* s, vm::Allocator* allocator): + s(s), allocator(allocator), listener(0) + { } + + virtual int64_t value() { + abort(s); + } + + virtual bool resolved() { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + vm::System* s; + vm::Allocator* allocator; + Listener* listener; + Promise* promise; +}; + +class DelayedPromise: public ListenPromise { + public: + DelayedPromise(vm::System* s, vm::Allocator* allocator, Promise* basis, + DelayedPromise* next): + ListenPromise(s, allocator), basis(basis), next(next) + { } + + virtual int64_t value() { + abort(s); + } + + virtual bool resolved() { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + Promise* basis; + DelayedPromise* next; +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_PROMISE_H \ No newline at end of file diff --git a/include/avian/vm/codegen/registers.h b/include/avian/vm/codegen/registers.h new file mode 100644 index 0000000000..495d8e75e1 --- /dev/null +++ b/include/avian/vm/codegen/registers.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_REGISTERS_H +#define AVIAN_CODEGEN_REGISTERS_H + +#include "avian/common.h" + +namespace avian { +namespace codegen { + +class RegisterMask { +public: + uint32_t mask; + uint8_t start; + uint8_t limit; + + static unsigned maskStart(uint32_t mask); + static unsigned maskLimit(uint32_t mask); + + inline RegisterMask(uint32_t mask): + mask(mask), + start(maskStart(mask)), + limit(maskLimit(mask)) + { } +}; + +class RegisterFile { +public: + RegisterMask allRegisters; + RegisterMask generalRegisters; + RegisterMask floatRegisters; + + inline RegisterFile(uint32_t generalRegisterMask, uint32_t floatRegisterMask): + allRegisters(generalRegisterMask | floatRegisterMask), + generalRegisters(generalRegisterMask), + floatRegisters(floatRegisterMask) + { } +}; + +class RegisterIterator { +public: + int index; + const RegisterMask& mask; + + inline RegisterIterator(const RegisterMask& mask): + index(mask.start), + mask(mask) {} + + inline bool hasNext() { + return index < mask.limit; + } + + inline int next() { + int r = index; + do { + index++; + } while(index < mask.limit && !(mask.mask & (1 << index))); + return r; + } +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_REGISTERS_H \ No newline at end of file diff --git a/include/avian/vm/codegen/targets.h b/include/avian/vm/codegen/targets.h new file mode 100644 index 0000000000..a8adb246ce --- /dev/null +++ b/include/avian/vm/codegen/targets.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_TARGETS_H +#define AVIAN_CODEGEN_TARGETS_H + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { + +class Architecture; + +Architecture* makeArchitectureNative(vm::System* system, bool useNativeFeatures); + +Architecture* makeArchitectureX86(vm::System* system, bool useNativeFeatures); +Architecture* makeArchitectureArm(vm::System* system, bool useNativeFeatures); +Architecture* makeArchitecturePowerpc(vm::System* system, bool useNativeFeatures); + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_TARGETS_H diff --git a/src/heap.h b/include/avian/vm/heap/heap.h similarity index 78% rename from src/heap.h rename to include/avian/vm/heap/heap.h index c3f1f8b4c8..874d40980e 100644 --- a/src/heap.h +++ b/include/avian/vm/heap/heap.h @@ -11,8 +11,8 @@ #ifndef HEAP_H #define HEAP_H -#include "system.h" -#include "allocator.h" +#include +#include "avian/allocator.h" namespace vm { @@ -59,13 +59,16 @@ class Heap: public Allocator { virtual void setClient(Client* client) = 0; virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) = 0; - virtual bool limitExceeded() = 0; - virtual void collect(CollectionType type, unsigned footprint) = 0; + virtual unsigned limit() = 0; + virtual bool limitExceeded(int pendingAllocation = 0) = 0; + virtual void collect(CollectionType type, unsigned footprint, + int pendingAllocation) = 0; + virtual unsigned fixedFootprint(unsigned sizeInWords, bool objectMask) = 0; virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords, - bool objectMask, unsigned* totalInBytes) = 0; + bool objectMask) = 0; virtual void* allocateImmortalFixed(Allocator* allocator, - unsigned sizeInWords, bool objectMask, - unsigned* totalInBytes) = 0; + unsigned sizeInWords, + bool objectMask) = 0; virtual void mark(void* p, unsigned offset, unsigned count) = 0; virtual void pad(void* p) = 0; virtual void* follow(void* p) = 0; diff --git a/src/system.h b/include/avian/vm/system/system.h similarity index 90% rename from src/system.h rename to include/avian/vm/system/system.h index c36aefacd9..5404a24109 100644 --- a/src/system.h +++ b/include/avian/vm/system/system.h @@ -11,12 +11,13 @@ #ifndef SYSTEM_H #define SYSTEM_H -#include "common.h" -#include "allocator.h" +#include "avian/common.h" +#include "avian/allocator.h" +#include namespace vm { -class System { +class System : public avian::util::Aborter { public: typedef intptr_t Status; @@ -121,8 +122,10 @@ class System { virtual bool success(Status) = 0; virtual void* tryAllocate(unsigned sizeInBytes) = 0; virtual void free(const void* p) = 0; +#if !defined(AVIAN_AOT_ONLY) virtual void* tryAllocateExecutable(unsigned sizeInBytes) = 0; virtual void freeExecutable(const void* p, unsigned sizeInBytes) = 0; +#endif virtual Status attach(Runnable*) = 0; virtual Status start(Runnable*) = 0; virtual Status make(Mutex**) = 0; @@ -148,7 +151,6 @@ class System { virtual int64_t now() = 0; virtual void yield() = 0; virtual void exit(int code) = 0; - virtual void abort() = 0; virtual void dispose() = 0; }; @@ -163,11 +165,8 @@ allocate(System* s, unsigned size) #define ACQUIRE_MONITOR(t, m) \ System::MonitorResource MAKE_NAME(monitorResource_) (t, m) -inline void NO_RETURN -abort(System* s) -{ - s->abort(); // this should not return - ::abort(); +inline avian::util::Aborter* getAborter(System* s) { + return s; } inline void NO_RETURN @@ -176,28 +175,22 @@ sysAbort(System* s) abort(s); } -inline void -expect(System* s, bool v) -{ - if (UNLIKELY(not v)) abort(s); -} +// #ifdef NDEBUG -#ifdef NDEBUG +// # define assert(a, b) +// # define vm_assert(a, b) -# define assert(a, b) -# define vm_assert(a, b) +// #else // not NDEBUG -#else // not NDEBUG +// inline void +// assert(System* s, bool v) +// { +// expect(s, v); +// } -inline void -assert(System* s, bool v) -{ - expect(s, v); -} +// # define vm_assert(a, b) vm::assert(a, b) -# define vm_assert(a, b) vm::assert(a, b) - -#endif // not NDEBUG +// #endif // not NDEBUG JNIEXPORT System* makeSystem(const char* crashDumpDirectory); diff --git a/makefile b/makefile index 7782850780..93415db230 100755 --- a/makefile +++ b/makefile @@ -25,6 +25,8 @@ bootimage-platform = \ $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) platform = $(bootimage-platform) +codegen-targets = native + mode = fast process = compile @@ -49,16 +51,24 @@ endif ifeq ($(continuations),true) options := $(options)-continuations endif +ifeq ($(codegen-targets),all) + options := $(options)-all +endif +aot-only = false root := $(shell (cd .. && pwd)) build = build/$(platform)-$(arch)$(options) +host-build-root = $(build)/host classpath-build = $(build)/classpath test-build = $(build)/test src = src classpath-src = classpath test = test +unittest = unittest win32 ?= $(root)/win32 win64 ?= $(root)/win64 +winrt ?= $(root)/winrt +wp8 ?= $(root)/wp8 classpath = avian @@ -120,6 +130,8 @@ ifneq ($(openjdk),) endif javahome-object = $(build)/javahome-jar.o boot-javahome-object = $(build)/boot-javahome.o + stub-sources = $(src)/openjdk/stubs.cpp + stub-objects = $(call cpp-objects,$(stub-sources),$(src),$(build)) else options := $(options)-openjdk test-executable = $(shell pwd)/$(executable-dynamic) @@ -133,11 +145,91 @@ ifneq ($(openjdk),) javahome = "$$($(native-path) "$(openjdk)/jre")" endif - classpath = openjdk + classpath = openjdk boot-classpath := "$(boot-classpath)$(path-separator)$$($(native-path) "$(openjdk)/jre/lib/rt.jar")" build-javahome = $(openjdk)/jre endif +ifneq ($(android),) + options := $(options)-android + classpath-jar-dep = $(build)/android.dep + luni-native = $(android)/libcore/luni/src/main/native + classpath-cflags = -DBOOT_JAVAHOME + android-cflags := -I$(luni-native) \ + -I$(android)/libnativehelper/include/nativehelper \ + -I$(android)/system/core/include \ + -I$(android)/external/zlib \ + -I$(android)/external/icu4c/i18n \ + -I$(android)/external/icu4c/common \ + -I$(android)/external/expat \ + -I$(android)/external/openssl/include \ + -I$(android)/libcore/include \ + -I$(build)/android-src/external/fdlibm \ + -I$(build)/android-src \ + -fno-exceptions \ + -D_FILE_OFFSET_BITS=64 \ + -g3 \ + -Werror + + luni-cpps := $(shell find $(luni-native) -name '*.cpp') + + ifeq ($(platform),windows) + android-cflags += -D__STDC_CONSTANT_MACROS + ifneq ($(arch),i386) + android-cflags += -fPIC + endif + blacklist = $(luni-native)/java_io_Console.cpp \ + $(luni-native)/java_lang_ProcessManager.cpp \ + $(luni-native)/libcore_io_OsConstants.cpp \ + $(luni-native)/libcore_io_Posix.cpp \ + $(luni-native)/libcore_io_AsynchronousCloseMonitor.cpp \ + $(luni-native)/libcore_net_RawSocket.cpp \ + $(luni-native)/org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp \ + $(luni-native)/AsynchronousSocketCloseMonitor.cpp \ + $(luni-native)/NetworkUtilities.cpp + luni-cpps := $(filter-out $(blacklist),$(luni-cpps)) + icu-libs := $(android)/external/icu4c/lib/sicuin.a \ + $(android)/external/icu4c/lib/sicuuc.a \ + $(android)/external/icu4c/lib/sicudt.a + platform-lflags := -lgdi32 + else + android-cflags += -fPIC -DHAVE_SYS_UIO_H + icu-libs := $(android)/external/icu4c/lib/libicui18n.a \ + $(android)/external/icu4c/lib/libicuuc.a \ + $(android)/external/icu4c/lib/libicudata.a + endif + + classpath-lflags := \ + $(icu-libs) \ + $(android)/external/fdlibm/libfdm.a \ + $(android)/external/expat/.libs/libexpat.a \ + $(android)/openssl-upstream/libssl.a \ + $(android)/openssl-upstream/libcrypto.a \ + $(platform-lflags) \ + -lstdc++ + + classpath-objects = \ + $(call cpp-objects,$(luni-cpps),$(luni-native),$(build)) + luni-java = $(android)/libcore/luni/src/main/java + luni-javas := $(shell find $(luni-java) -name '*.java') + dalvik-java = $(android)/libcore/dalvik/src/main/java + dalvik-javas := $(shell find $(dalvik-java) -name '*.java') + xml-java = $(android)/libcore/xml/src/main/java + xml-javas := $(shell find $(xml-java) -name '*.java') + android-classes = \ + $(call java-classes,$(luni-javas),$(luni-java),$(build)/android) \ + $(call java-classes,$(dalvik-javas),$(dalvik-java),$(build)/android) \ + $(call java-classes,$(xml-javas),$(xml-java),$(build)/android) + classpath = android + + javahome-files = tzdata + javahome-object = $(build)/javahome-jar.o + boot-javahome-object = $(build)/boot-javahome.o + build-javahome = $(android)/bionic/libc/zoneinfo + stub-sources = $(src)/android/stubs.cpp + stub-objects = $(call cpp-objects,$(stub-sources),$(src),$(build)) +endif + ifeq ($(classpath),avian) jni-sources := $(shell find $(classpath-src) -name '*.cpp') jni-objects = $(call cpp-objects,$(jni-sources),$(classpath-src),$(build)) @@ -175,7 +267,7 @@ dlltool = dlltool vg = nice valgrind --num-callers=32 --db-attach=yes --freelist-vol=100000000 vg += --leak-check=full --suppressions=valgrind.supp db = gdb --args -javac = "$(JAVA_HOME)/bin/javac" +javac = "$(JAVA_HOME)/bin/javac" -encoding UTF-8 javah = "$(JAVA_HOME)/bin/javah" jar = "$(JAVA_HOME)/bin/jar" strip = strip @@ -183,6 +275,18 @@ strip-all = --strip-all rdynamic = -rdynamic +cflags_debug = -O0 -g3 +cflags_debug_fast = -O0 -g3 +cflags_stress = -O0 -g3 +cflags_stress_major = -O0 -g3 +ifeq ($(use-clang),true) + cflags_fast = -O4 -g3 + cflags_small = -Oz -g3 +else + cflags_fast = -O3 -g3 + cflags_small = -Os -g3 +endif + # note that we suppress the non-virtual-dtor warning because we never # use the delete operator, which means we don't need virtual # destructors: @@ -192,12 +296,13 @@ warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self \ target-cflags = -DTARGET_BYTES_PER_WORD=$(pointer-size) common-cflags = $(warnings) -fno-rtti -fno-exceptions -I$(classpath-src) \ - "-I$(JAVA_HOME)/include" -idirafter $(src) -I$(build) $(classpath-cflags) \ + "-I$(JAVA_HOME)/include" -I$(src) -I$(build) -Iinclude $(classpath-cflags) \ -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ + -DAVIAN_INFO="\"$(info)\"" \ -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" $(target-cflags) -asmflags = $(target-cflags) +asmflags = $(target-cflags) -I$(src) ifneq (,$(filter i386 x86_64,$(arch))) ifeq ($(use-frame-pointer),true) @@ -209,7 +314,7 @@ endif build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread -converter-cflags = -D__STDC_CONSTANT_MACROS -Isrc/binaryToObject -Isrc/ \ +converter-cflags = -D__STDC_CONSTANT_MACROS -Iinclude/ -Isrc/ \ -fno-rtti -fno-exceptions \ -DAVIAN_TARGET_ARCH=AVIAN_ARCH_UNKNOWN \ -DAVIAN_TARGET_FORMAT=AVIAN_FORMAT_UNKNOWN \ @@ -217,12 +322,13 @@ converter-cflags = -D__STDC_CONSTANT_MACROS -Isrc/binaryToObject -Isrc/ \ cflags = $(build-cflags) -common-lflags = -lm -lz $(classpath-lflags) +common-lflags = -lm -lz build-lflags = -lz -lpthread -ldl lflags = $(common-lflags) -lpthread -ldl +soname-flag = -Wl,-soname -Wl,$(so-prefix)jvm$(so-suffix) version-script-flag = -Wl,--version-script=openjdk.ld build-system = posix @@ -235,13 +341,44 @@ pointer-size = 8 so-prefix = lib so-suffix = .so +static-prefix = lib +static-suffix = .a + +output = -o $(1) +asm-output = -o $(1) +asm-input = -c $(1) +asm-format = S +as = $(cc) +ld = $(cc) +build-ld = $(build-cc) + +default-remote-test-host = localhost +default-remote-test-port = 22 +ifeq ($(remote-test-host),) + remote-test-host = $(default-remote-test-host) +else + remote-test = true +endif +ifeq ($(remote-test-port),) + remote-test-port = $(default-remote-test-port) +else + remote-test = true +endif +remote-test-user = ${USER} +remote-test-dir = /tmp/avian-test-${USER} + +static = -static shared = -shared +rpath = -Wl,-rpath=\$$ORIGIN -Wl,-z,origin + no-error = -Wno-error openjdk-extra-cflags = -fvisibility=hidden bootimage-cflags = -DTARGET_BYTES_PER_WORD=$(pointer-size) +bootimage-symbols = _binary_bootimage_bin_start:_binary_bootimage_bin_end +codeimage-symbols = _binary_codeimage_bin_start:_binary_codeimage_bin_end developer-dir := $(shell if test -d /Developer; then echo /Developer; \ else echo /Applications/Xcode.app/Contents/Developer; fi) @@ -289,8 +426,13 @@ ifeq ($(arch),arm) ifneq ($(arch),$(build-arch)) ifeq ($(platform),darwin) ios-bin = $(developer-dir)/Platforms/iPhoneOS.platform/Developer/usr/bin - cxx = $(ios-bin)/g++ - cc = $(ios-bin)/gcc + ifeq ($(use-clang),true) + cxx = clang -std=c++11 + cc = clang + else + cxx = $(ios-bin)/g++ + cc = $(ios-bin)/gcc + endif ar = $(ios-bin)/ar ranlib = $(ios-bin)/ranlib strip = $(ios-bin)/strip @@ -310,8 +452,10 @@ endif ifeq ($(build-platform),darwin) build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) - cflags += -I/System/Library/Frameworks/JavaVM.framework/Headers/ + cflags += -I/System/Library/Frameworks/JavaVM.framework/Headers/ \ + -Wno-deprecated-declarations build-lflags += -framework CoreFoundation + soname-flag = endif ifeq ($(platform),qnx) @@ -344,6 +488,70 @@ ifeq ($(platform),freebsd) "-I$(JAVA_HOME)/include/freebsd" -I$(src) -pthread cflags = $(build-cflags) endif +ifeq ($(platform),android) + ifeq ($(build-platform),cygwin) + ndk = "$$(cygpath -u "$(ANDROID_NDK)")" + else + ndk = $(ANDROID_NDK) + endif + + ifeq ($(android-version),) + android-version = 5 + endif + + ifeq ($(android-toolchain),) + android-toolchain = 4.7 + endif + + ifeq ($(arch),arm) + android-toolchain-name = arm-linux-androideabi + android-toolchain-prefix = arm-linux-androideabi- + endif + ifeq ($(arch),i386) + android-toolchain-name = x86 + android-toolchain-prefix = i686-linux-android- + endif + + ifeq ($(android-arm-arch),) + android-arm-arch = armv5 + endif + + options := $(options)-api$(android-version)-$(android-toolchain)-$(android-arm-arch) + + build-cflags = $(common-cflags) -I$(src) + build-lflags = -lz -lpthread + ifeq ($(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))),windows) + toolchain-host-platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform))) + build-system = windows + build-cxx = i686-w64-mingw32-g++ + build-cc = i686-w64-mingw32-gcc + sysroot = "$$(cygpath -w "$(ndk)/platforms/android-$(android-version)/arch-arm")" + build-cflags += "-I$(JAVA_HOME)/include/win32" + else + toolchain-host-platform = $(subst cygwin,windows,$(subst mingw32,windows,$(build-platform)))-* + sysroot = $(ndk)/platforms/android-$(android-version)/arch-arm + build-cflags += "-I$(JAVA_HOME)/include/linux" + build-lflags += -ldl + endif + toolchain = $(ndk)/toolchains/$(android-toolchain-name)-$(android-toolchain)/prebuilt/$(toolchain-host-platform) + cflags = "-I$(sysroot)/usr/include" "-I$(JAVA_HOME)/include/linux" $(common-cflags) "-I$(src)" -std=c++11 $(no-psabi) + lflags = "-L$(sysroot)/usr/lib" $(common-lflags) -llog + target-format = elf + use-lto = false + + ifeq ($(arch),arm) + cflags += -marm -march=$(android-arm-arch) -ftree-vectorize -ffast-math -mfloat-abi=softfp + endif + ifeq ($(arch),i386) + endif + + cxx = $(toolchain)/bin/$(android-toolchain-prefix)g++ --sysroot="$(sysroot)" + cc = $(toolchain)/bin/$(android-toolchain-prefix)gcc --sysroot="$(sysroot)" + as = $(cxx) + ar = $(toolchain)/bin/$(android-toolchain-prefix)ar + ranlib = $(toolchain)/bin/$(android-toolchain-prefix)ranlib + strip = $(toolchain)/bin/$(android-toolchain-prefix)strip +endif ifeq ($(platform),darwin) target-format = macho @@ -382,12 +590,15 @@ ifeq ($(platform),darwin) strip-all = -S -x so-suffix = .dylib shared = -dynamiclib + rpath = sdk-dir = $(developer-dir)/Platforms/iPhoneOS.platform/Developer/SDKs ifeq ($(arch),arm) ios-version := \ - $(shell if test -d $(sdk-dir)/iPhoneOS5.1.sdk; then echo 5.1; \ + $(shell if test -d $(sdk-dir)/iPhoneOS6.1.sdk; then echo 6.1; \ + elif test -d $(sdk-dir)/iPhoneOS6.0.sdk; then echo 6.0; \ + elif test -d $(sdk-dir)/iPhoneOS5.1.sdk; then echo 5.1; \ elif test -d $(sdk-dir)/iPhoneOS5.0.sdk; then echo 5.0; \ elif test -d $(sdk-dir)/iPhoneOS4.3.sdk; then echo 4.3; \ elif test -d $(sdk-dir)/iPhoneOS4.2.sdk; then echo 4.2; \ @@ -399,34 +610,37 @@ ifeq ($(platform),darwin) flags = -arch armv7 -isysroot \ $(sdk-dir)/iPhoneOS$(ios-version).sdk/ - openjdk-extra-cflags += $(flags) + classpath-extra-cflags += $(flags) cflags += $(flags) asmflags += $(flags) lflags += $(flags) endif ifeq ($(arch),powerpc) - openjdk-extra-cflags += -arch ppc -mmacosx-version-min=${OSX_SDK_VERSION} + classpath-extra-cflags += -arch ppc -mmacosx-version-min=${OSX_SDK_VERSION} cflags += -arch ppc -mmacosx-version-min=${OSX_SDK_VERSION} asmflags += -arch ppc -mmacosx-version-min=${OSX_SDK_VERSION} lflags += -arch ppc -mmacosx-version-min=${OSX_SDK_VERSION} endif ifeq ($(arch),i386) - openjdk-extra-cflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} + classpath-extra-cflags += \ + -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} cflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} asmflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} lflags += -arch i386 -mmacosx-version-min=${OSX_SDK_VERSION} endif ifeq ($(arch),x86_64) - openjdk-extra-cflags += -arch x86_64 + classpath-extra-cflags += -arch x86_64 cflags += -arch x86_64 asmflags += -arch x86_64 lflags += -arch x86_64 endif endif +openjdk-extra-cflags += $(classpath-extra-cflags) + ifeq ($(platform),windows) target-format = pe @@ -434,14 +648,14 @@ ifeq ($(platform),windows) lib = "$(win32)/lib" embed-prefix = c:/avian-embedded - system = windows so-prefix = so-suffix = .dll exe-suffix = .exe + rpath = - lflags = -L$(lib) $(common-lflags) -lws2_32 -liphlpapi -mwindows -mconsole + lflags = -L$(lib) $(common-lflags) -lws2_32 -liphlpapi -mconsole cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 ifeq (,$(filter mingw32 cygwin,$(build-platform))) @@ -486,40 +700,281 @@ ifeq ($(platform),windows) else shared += -Wl,--add-stdcall-alias endif + + embed = $(build-embed)/embed$(exe-suffix) + embed-loader = $(build-embed-loader)/embed-loader$(exe-suffix) + embed-loader-o = $(build-embed)/embed-loader.o +endif + +ifeq ($(platform),wp8) + ifeq ($(shell uname -s | grep -i -c WOW64),1) + programFiles = Program Files (x86) + else + programFiles = Program Files + endif + ifeq ($(MSVS_ROOT),) + # Environment variable MSVS_ROOT not found. It should be something like + # "C:\$(programFiles)\Microsoft Visual Studio 11.0" + MSVS_ROOT = C:\$(programFiles)\Microsoft Visual Studio 11.0 + endif + ifeq ($(MSVC_ROOT),) + # Environment variable MSVC_ROOT not found. It should be something like + # "C:\$(programFiles)\Microsoft Visual Studio 11.0\VC" + MSVC_ROOT = $(MSVS_ROOT)\VC + endif + ifeq ($(WP80_SDK),) + # Environment variable WP8_SDK not found. It should be something like + # "C:\Program Files[ (x86)]\Microsoft Visual Studio 11.0\VC\WPSDK\WP80" + # TODO: Lookup in SOFTWARE\Microsoft\Microsoft SDKs\WindowsPhone\v8.0 + WP80_SDK = $(MSVS_ROOT)\VC\WPSDK\WP80 + endif + ifeq ($(WP80_KIT),) + # Environment variable WP8_KIT not found. It should be something like + # "c:\Program Files[ (x86)]\Windows Phone Kits\8.0" + # TODO: Lookup in SOFTWARE\Microsoft\Microsoft SDKs\WindowsPhone\v8.0 + WP80_KIT = C:\$(programFiles)\Windows Phone Kits\8.0 + endif + ifeq ($(WIN8_KIT),) + # Environment variable WIN8_KIT not found. It should be something like + # "c:\Program Files[ (x86)]\Windows Kits\8.0" + WIN8_KIT = C:\$(programFiles)\Windows Kits\8.0 + endif + ifeq ($(build-platform),cygwin) + windows-path = cygpath -w + else + windows-path = $(native-path) + endif + windows-java-home := $(shell $(windows-path) "$(JAVA_HOME)") + target-format = pe + ms_cl_compiler = wp8 + use-lto = false + supports_avian_executable = false + aot-only = true + ifneq ($(bootimage),true) + x := $(error Windows Phone 8 target requires bootimage=true) + endif + system = windows + build-system = windows + static-prefix = + static-suffix = .lib + so-prefix = + so-suffix = .dll + exe-suffix = .exe + manifest-flags = -MANIFEST:NO + + ifeq ($(arch),arm) + wp8_arch = \x86_arm + vc_arch = \arm + w8kit_arch = arm + deps_arch = ARM + as = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\armasm.exe")" + cxx = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\cl.exe")" + ld = "$$(cygpath -u "$(WP80_SDK)\bin\x86_arm\link.exe")" + asmflags = -machine ARM -32 + asm-output = -o $(1) + asm-input = $(1) + machine_type = ARM + bootimage-symbols = binary_bootimage_bin_start:binary_bootimage_bin_end + codeimage-symbols = binary_codeimage_bin_start:binary_codeimage_bin_end + endif + ifeq ($(arch),i386) + wp8_arch = + vc_arch = + w8kit_arch = x86 + deps_arch = x86 + asmflags = $(target-cflags) -safeseh -nologo -Gd + as = "$$(cygpath -u "$(WP80_SDK)\bin\ml.exe")" + cxx = "$$(cygpath -u "$(WP80_SDK)\bin\cl.exe")" + ld = "$$(cygpath -u "$(WP80_SDK)\bin\link.exe")" + ifeq ($(mode),debug) + asmflags += -Zd + endif + ifeq ($(mode),debug-fast) + asmflags += -Zd + endif + asm-output = $(output) + machine_type = X86 + endif + + PATH := $(shell cygpath -u "$(MSVS_ROOT)\Common7\IDE"):$(shell cygpath -u "$(WP80_SDK)\bin$(wp8_arch)"):$(shell cygpath -u "$(WP80_SDK)\bin"):${PATH} + + build-cflags = $(common-cflags) -I$(src) -I$(inc) -mthreads + build-lflags = -lz -lpthread + + cflags = -nologo \ + -AI"$(WP80_KIT)\Windows Metadata" \ + -I"$(WP80_SDK)\include" -I"$(WP80_KIT)\Include" -I"$(WP80_KIT)\Include\minwin" -I"$(WP80_KIT)\Include\mincore" \ + -DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP -D_USRDLL -D_WINDLL \ + -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ + -I"$(shell $(windows-path) "$(wp8)/zlib/upstream")" -I"$(shell $(windows-path) "$(wp8)/interop/avian-interop-client")" \ + -I"$(shell $(windows-path) "$(wp8)/include")" -I$(src) -I$(classpath-src) \ + -I"$(build)" \ + -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" \ + -DTARGET_BYTES_PER_WORD=$(pointer-size) \ + -Gd -EHsc + + common-lflags = + + ifeq ($(mode),debug) + build-type = Debug + endif + ifeq ($(mode),debug-fast) + build-type = Debug + endif + ifeq ($(mode),stress_major) + build-type = Release + endif + ifeq ($(mode),fast) + build-type = Release + endif + ifeq ($(mode),fast) + build-type = Release + endif + ifeq ($(mode),small) + build-type = Release + endif + + arflags = -MACHINE:$(machine_type) + lflags = $(common-lflags) -nologo \ + -MACHINE:$(machine_type) \ + -LIBPATH:"$(WP80_KIT)\lib\$(w8kit_arch)" -LIBPATH:"$(WP80_SDK)\lib$(vc_arch)" -LIBPATH:"$(WIN8_KIT)\Lib\win8\um\$(w8kit_arch)" \ + ws2_32.lib \ + "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\zlib.lib")" "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\ThreadEmulation.lib")" \ + "$(shell $(windows-path) "$(wp8)\lib\$(deps_arch)\$(build-type)\AvianInteropClient.lib")" + lflags += -NXCOMPAT -DYNAMICBASE -SUBSYSTEM:CONSOLE -TLBID:1 + lflags += -NODEFAULTLIB:"ole32.lib" -NODEFAULTLIB:"kernel32.lib" + lflags += PhoneAppModelHost.lib WindowsPhoneCore.lib -WINMD -WINMDFILE:$(subst $(so-suffix),.winmd,$(@)) + + cc = $(cxx) + asm-format = masm + shared = -dll + ar = "$$(cygpath -u "$(WP80_SDK)\bin\lib.exe")" + arflags += -nologo + ifeq ($(build-platform),cygwin) + build-cxx = i686-w64-mingw32-g++ + build-cc = i686-w64-mingw32-gcc + dlltool = i686-w64-mingw32-dlltool + ranlib = + strip = + endif + output = -Fo$(1) + + #TODO: -MT or -ZW? + cflags_debug = -Od -Zi -MDd + cflags_debug_fast = -Od -Zi -MDd + cflags_stress = -O0 -g3 -MD + cflags_stress_major = -O0 -g3 -MD + cflags_fast = -O2 -Zi -MD + cflags_small = -O1s -Zi -MD + # -GL [whole program optimization] in 'fast' and 'small' breaks compilation for some reason + + ifeq ($(mode),debug) + cflags += + lflags += + endif + ifeq ($(mode),debug-fast) + cflags += -DNDEBUG + lflags += + endif + ifeq ($(mode),stress_major) + cflags += + lflags += + endif + ifeq ($(mode),fast) + cflags += + lflags += + endif + # -LTCG is needed only if -GL is used + ifeq ($(mode),fast) + cflags += -DNDEBUG + lflags += -LTCG + arflags += + endif + ifeq ($(mode),small) + cflags += -DNDEBUG + lflags += -LTCG + arflags += + endif + + strip = : +endif + +ifdef msvc + no-error = + target-format = pe + windows-path = $(native-path) + windows-java-home := $(shell $(windows-path) "$(JAVA_HOME)") + zlib := $(shell $(windows-path) "$(win32)/msvc") + ms_cl_compiler = regular + as = $(build-cc) + cxx = "$(msvc)/BIN/cl.exe" + cc = $(cxx) + ld = "$(msvc)/BIN/link.exe" + mt = "mt.exe" + ar = "$(msvc)/BIN/lib.exe" + manifest-flags = -MANIFEST -MANIFESTFILE:$(@).manifest + cflags = -nologo -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ + -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ + -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ + -Fd$(build)/$(name).pdb -I"$(zlib)/include" -I$(src) -I$(classpath-src) \ + -I"$(build)" -Iinclude \ + -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" \ + -DTARGET_BYTES_PER_WORD=$(pointer-size) + + ifneq ($(lzma),) + cflags += -I$(shell $(windows-path) "$(lzma)") + endif + + shared = -dll + lflags = -nologo -LIBPATH:"$(zlib)/lib" -DEFAULTLIB:ws2_32 \ + -DEFAULTLIB:zlib -DEFAULTLIB:user32 -MANIFEST -debug + output = -Fo$(1) + + cflags_debug = -Od -Zi -MDd + cflags_debug_fast = -Od -Zi -DNDEBUG + cflags_fast = -O2 -GL -Zi -DNDEBUG + cflags_small = -O1s -Zi -GL -DNDEBUG + ifeq ($(mode),fast) + lflags += -LTCG + endif + ifeq ($(mode),small) + lflags += -LTCG + endif + + use-lto = false + strip = : endif ifeq ($(mode),debug) - optimization-cflags = -O0 -g3 - converter-cflags += -O0 -g3 + optimization-cflags = $(cflags_debug) + converter-cflags += $(cflags_debug) strip = : endif ifeq ($(mode),debug-fast) - optimization-cflags = -O0 -g3 -DNDEBUG + optimization-cflags = $(cflags_debug_fast) -DNDEBUG strip = : endif ifeq ($(mode),stress) - optimization-cflags = -O0 -g3 -DVM_STRESS + optimization-cflags = $(cflags_stress) -DVM_STRESS strip = : endif ifeq ($(mode),stress-major) - optimization-cflags = -O0 -g3 -DVM_STRESS -DVM_STRESS_MAJOR + optimization-cflags = $(cflags_stress_major) -DVM_STRESS -DVM_STRESS_MAJOR strip = : endif ifeq ($(mode),fast) - ifeq ($(use-clang),true) - optimization-cflags = -O4 -g3 -DNDEBUG - else - optimization-cflags = -O3 -g3 -DNDEBUG + optimization-cflags = $(cflags_fast) -DNDEBUG + ifeq ($(use-lto),) + use-lto = true endif - use-lto = true endif ifeq ($(mode),small) - ifeq ($(use-clang),true) - optimization-cflags = -Oz -g3 -DNDEBUG - else - optimization-cflags = -Os -g3 -DNDEBUG + optimization-cflags = $(cflags_small) -DNDEBUG + ifeq ($(use-lto),) + use-lto = true endif - use-lto = true endif ifeq ($(use-lto),true) @@ -541,6 +996,7 @@ endif cflags += $(optimization-cflags) +ifndef ms_cl_compiler ifneq ($(platform),darwin) ifeq ($(arch),i386) # this is necessary to support __sync_bool_compare_and_swap: @@ -548,59 +1004,11 @@ ifeq ($(arch),i386) lflags += -march=i586 endif endif - -output = -o $(1) -as := $(cc) -ld := $(cc) -build-ld := $(build-cc) - -ifdef msvc - no-error = - windows-path = $(native-path) - windows-java-home := $(shell $(windows-path) "$(JAVA_HOME)") - zlib := $(shell $(windows-path) "$(win32)/msvc") - cxx = "$(msvc)/BIN/cl.exe" - cc = $(cxx) - ld = "$(msvc)/BIN/link.exe" - mt = "mt.exe" - cflags = -nologo -DAVIAN_VERSION=\"$(version)\" -D_JNI_IMPLEMENTATION_ \ - -DUSE_ATOMIC_OPERATIONS -DAVIAN_JAVA_HOME=\"$(javahome)\" \ - -DAVIAN_EMBED_PREFIX=\"$(embed-prefix)\" \ - -Fd$(build)/$(name).pdb -I"$(zlib)/include" -I$(src) -I$(classpath-src) \ - -I"$(build)" \ - -I"$(windows-java-home)/include" -I"$(windows-java-home)/include/win32" \ - -DTARGET_BYTES_PER_WORD=$(pointer-size) - - ifneq ($(lzma),) - cflags += -I$(shell $(windows-path) "$(lzma)") - endif - - shared = -dll - lflags = -nologo -LIBPATH:"$(zlib)/lib" -DEFAULTLIB:ws2_32 \ - -DEFAULTLIB:zlib -MANIFEST -debug - output = -Fo$(1) - - ifeq ($(mode),debug) - cflags += -Od -Zi -MDd - endif - ifeq ($(mode),debug-fast) - cflags += -Od -Zi -DNDEBUG - endif - ifeq ($(mode),fast) - cflags += -O2 -GL -Zi -DNDEBUG - lflags += -LTCG - endif - ifeq ($(mode),small) - cflags += -O1s -Zi -GL -DNDEBUG - lflags += -LTCG - endif - - strip = : endif c-objects = $(foreach x,$(1),$(patsubst $(2)/%.c,$(3)/%.o,$(x))) cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(3)/%.o,$(x))) -asm-objects = $(foreach x,$(1),$(patsubst $(2)/%.S,$(3)/%-asm.o,$(x))) +asm-objects = $(foreach x,$(1),$(patsubst $(2)/%.$(asm-format),$(3)/%-asm.o,$(x))) java-classes = $(foreach x,$(1),$(patsubst $(2)/%.java,$(3)/%.class,$(x))) generated-code = \ @@ -612,33 +1020,78 @@ generated-code = \ $(build)/type-name-initializations.cpp \ $(build)/type-maps.cpp -vm-depends := $(generated-code) $(wildcard $(src)/*.h) +vm-depends := $(generated-code) \ + $(shell find src include -name '*.h' -or -name '*.inc.cpp') vm-sources = \ - $(src)/$(system).cpp \ + $(src)/vm/system/$(system).cpp \ $(src)/finder.cpp \ $(src)/machine.cpp \ $(src)/util.cpp \ - $(src)/heap.cpp \ + $(src)/heap/heap.cpp \ $(src)/$(process).cpp \ $(src)/classpath-$(classpath).cpp \ $(src)/builtin.cpp \ $(src)/jnienv.cpp \ $(src)/process.cpp -vm-asm-sources = $(src)/$(asm).S +vm-asm-sources = $(src)/$(asm).$(asm-format) target-asm = $(asm) -ifeq ($(process),compile) - vm-sources += \ - $(src)/compiler.cpp \ - $(src)/$(target-asm).cpp +build-embed = $(build)/embed +build-embed-loader = $(build)/embed-loader - vm-asm-sources += $(src)/compile-$(asm).S +embed-loader-sources = $(src)/embedded-loader.cpp +embed-loader-objects = $(call cpp-objects,$(embed-loader-sources),$(src),$(build-embed-loader)) + +embed-sources = $(src)/embed.cpp +embed-objects = $(call cpp-objects,$(embed-sources),$(src),$(build-embed)) + +compiler-sources = \ + $(src)/codegen/compiler.cpp \ + $(wildcard $(src)/codegen/compiler/*.cpp) \ + $(src)/codegen/registers.cpp \ + $(src)/codegen/targets.cpp + +x86-assembler-sources = $(wildcard $(src)/codegen/target/x86/*.cpp) + +arm-assembler-sources = $(wildcard $(src)/codegen/target/arm/*.cpp) + +powerpc-assembler-sources = $(wildcard $(src)/codegen/target/powerpc/*.cpp) + +all-assembler-sources = \ + $(x86-assembler-sources) \ + $(arm-assembler-sources) \ + $(powerpc-assembler-sources) + +native-assembler-sources = $($(target-asm)-assembler-sources) + +audit-codegen-sources = $(wildcard $(src)/tools/audit-codegen/*.cpp) + +all-codegen-target-sources = \ + $(compiler-sources) \ + $(native-assembler-sources) + +ifeq ($(process),compile) + vm-sources += $(compiler-sources) + + ifeq ($(codegen-targets),native) + vm-sources += $(native-assembler-sources) + endif + ifeq ($(codegen-targets),all) + vm-sources += $(all-assembler-sources) + endif + + vm-asm-sources += $(src)/compile-$(asm).$(asm-format) +endif +cflags += -DAVIAN_PROCESS_$(process) +ifeq ($(aot-only),true) + cflags += -DAVIAN_AOT_ONLY endif vm-cpp-objects = $(call cpp-objects,$(vm-sources),$(src),$(build)) +all-codegen-target-objects = $(call cpp-objects,$(all-codegen-target-sources),$(src),$(build)) vm-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(build)) vm-objects = $(vm-cpp-objects) $(vm-asm-objects) @@ -646,6 +1099,8 @@ heapwalk-sources = $(src)/heapwalk.cpp heapwalk-objects = \ $(call cpp-objects,$(heapwalk-sources),$(src),$(build)) +unittest-objects = $(call cpp-objects,$(unittest-sources),$(unittest),$(build)/unittest) + ifeq ($(heapdump),true) vm-sources += $(src)/heapdump.cpp vm-heapwalk-objects = $(heapwalk-objects) @@ -661,10 +1116,12 @@ ifeq ($(continuations),true) asmflags += -DAVIAN_CONTINUATIONS endif -bootimage-generator-sources = $(src)/bootimage.cpp +bootimage-generator-sources = $(src)/tools/bootimage-generator/main.cpp $(src)/util/arg-parser.cpp $(stub-sources) + ifneq ($(lzma),) bootimage-generator-sources += $(src)/lzma-encode.cpp endif + bootimage-generator-objects = \ $(call cpp-objects,$(bootimage-generator-sources),$(src),$(build)) bootimage-generator = $(build)/bootimage-generator @@ -696,8 +1153,8 @@ boot-object = $(build)/boot.o generator-depends := $(wildcard $(src)/*.h) generator-sources = \ - $(src)/type-generator.cpp \ - $(src)/$(build-system).cpp \ + $(src)/tools/type-generator/main.cpp \ + $(src)/vm/system/$(build-system).cpp \ $(src)/finder.cpp ifneq ($(lzma),) @@ -751,27 +1208,32 @@ generator-lzma-objects = \ $(call generator-c-objects,$(lzma-decode-sources),$(lzma)/C,$(build)) generator = $(build)/generator -converter-depends = \ - $(src)/binaryToObject/tools.h \ - $(src)/binaryToObject/endianness.h +all-depends = $(shell find include -name '*.h') -converter-sources = \ - $(src)/binaryToObject/tools.cpp \ - $(src)/binaryToObject/elf.cpp \ - $(src)/binaryToObject/mach-o.cpp \ - $(src)/binaryToObject/pe.cpp +object-writer-depends = $(shell find $(src)/tools/object-writer -name '*.h') +object-writer-sources = $(shell find $(src)/tools/object-writer -name '*.cpp') +object-writer-objects = $(call cpp-objects,$(object-writer-sources),$(src),$(build)) -converter-tool-sources = \ - $(src)/binaryToObject/main.cpp +binary-to-object-depends = $(shell find $(src)/tools/binary-to-object/ -name '*.h') +binary-to-object-sources = $(shell find $(src)/tools/binary-to-object/ -name '*.cpp') +binary-to-object-objects = $(call cpp-objects,$(binary-to-object-sources),$(src),$(build)) + +converter-sources = $(object-writer-sources) + +converter-tool-depends = $(binary-to-object-depends) $(all-depends) +converter-tool-sources = $(binary-to-object-sources) converter-objects = $(call cpp-objects,$(converter-sources),$(src),$(build)) converter-tool-objects = $(call cpp-objects,$(converter-tool-sources),$(src),$(build)) converter = $(build)/binaryToObject/binaryToObject -static-library = $(build)/lib$(name).a +static-library = $(build)/$(static-prefix)$(name)$(static-suffix) executable = $(build)/$(name)${exe-suffix} dynamic-library = $(build)/$(so-prefix)jvm$(so-suffix) -executable-dynamic = $(build)/$(name)-dynamic${exe-suffix} +executable-dynamic = $(build)/$(name)-dynamic$(exe-suffix) + +unittest-executable = $(build)/$(name)-unittest${exe-suffix} +audit-codegen-executable = $(build)/audit-codegen${exe-suffix} ifneq ($(classpath),avian) # Assembler, ConstantPool, and Stream are not technically needed for a @@ -779,6 +1241,7 @@ ifneq ($(classpath),avian) # them to synthesize a class: classpath-sources := \ $(classpath-src)/avian/Addendum.java \ + $(classpath-src)/avian/AnnotationInvocationHandler.java \ $(classpath-src)/avian/Assembler.java \ $(classpath-src)/avian/Callback.java \ $(classpath-src)/avian/CallbackReceiver.java \ @@ -793,14 +1256,24 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/Singleton.java \ $(classpath-src)/avian/Stream.java \ $(classpath-src)/avian/SystemClassLoader.java \ + $(classpath-src)/avian/Traces.java \ $(classpath-src)/avian/VMClass.java \ $(classpath-src)/avian/VMField.java \ $(classpath-src)/avian/VMMethod.java \ - $(classpath-src)/avian/avian_vm_resource/Handler.java + $(classpath-src)/avian/avianvmresource/Handler.java ifneq ($(openjdk),) classpath-sources := $(classpath-sources) \ $(classpath-src)/avian/OpenJDK.java + else + classpath-sources := $(classpath-sources) \ + $(classpath-src)/sun/reflect/ConstantPool.java \ + $(classpath-src)/java/lang/ReflectiveOperationException.java \ + $(classpath-src)/java/net/ProtocolFamily.java \ + $(classpath-src)/java/net/StandardProtocolFamily.java \ + $(classpath-src)/sun/misc/Cleaner.java \ + $(classpath-src)/sun/misc/Unsafe.java \ + $(classpath-src)/java/lang/reflect/Proxy.java endif else classpath-sources := $(shell find $(classpath-src) -name '*.java') @@ -830,6 +1303,14 @@ test-extra-classes = \ $(call java-classes,$(test-extra-sources),$(test),$(test-build)) test-extra-dep = $(test-build)-extra.dep +unittest-sources = \ + $(wildcard $(unittest)/*.cpp) \ + $(wildcard $(unittest)/util/*.cpp) \ + $(wildcard $(unittest)/codegen/*.cpp) + +unittest-depends = \ + $(wildcard $(unittest)/*.h) + ifeq ($(continuations),true) continuation-tests = \ extra.Continuations \ @@ -878,9 +1359,15 @@ test-flags = -Djava.library.path=$(build) -cp $(build)/test test-args = $(test-flags) $(input) .PHONY: build +ifneq ($(supports_avian_executable),false) build: $(static-library) $(executable) $(dynamic-library) $(lzma-loader) \ $(lzma-encoder) $(executable-dynamic) $(classpath-dep) $(test-dep) \ - $(test-extra-dep) + $(test-extra-dep) $(embed) +else +build: $(static-library) $(dynamic-library) $(lzma-loader) \ + $(lzma-encoder) $(classpath-dep) $(test-dep) \ + $(test-extra-dep) $(embed) +endif $(test-dep): $(classpath-dep) @@ -899,11 +1386,22 @@ vg: build $(library-path) $(vg) $(test-executable) $(test-args) .PHONY: test -test: build - $(library-path) /bin/sh $(test)/test.sh 2>/dev/null \ - $(test-executable) $(mode) "$(test-flags)" \ - $(call class-names,$(test-build),$(filter-out $(test-support-classes), $(test-classes))) \ - $(continuation-tests) $(tail-tests) +test: build $(build)/run-tests.sh $(build)/test.sh $(unittest-executable) +ifneq ($(remote-test),true) + /bin/sh $(build)/run-tests.sh +else + @echo "running tests on $(remote-test-user)@$(remote-test-host):$(remote-test-port), in $(remote-test-dir)" + rsync $(build) -rav --exclude '*.o' --rsh="ssh -p$(remote-test-port)" $(remote-test-user)@$(remote-test-host):$(remote-test-dir) + ssh -p$(remote-test-port) $(remote-test-user)@$(remote-test-host) sh "$(remote-test-dir)/$(platform)-$(arch)$(options)/run-tests.sh" +endif + +PHONY: audit-baseline +audit-baseline: $(audit-codegen-executable) + $(<) -output $(build)/codegen-audit-output/baseline.o -format macho + +PHONY: audit +audit: $(audit-codegen-executable) + $(<) -output $(build)/codegen-audit-output/baseline.o -format macho .PHONY: tarball tarball: @@ -920,12 +1418,29 @@ javadoc: -header "Avian v$(version)" \ -bottom "http://oss.readytalk.com/avian" +.PHONY: clean-current +clean-current: + @echo "removing $(build)" + rm -rf $(build) + .PHONY: clean clean: @echo "removing build" rm -rf build -$(build)/compile-x86-asm.o: $(src)/continuations-x86.S +ifeq ($(continuations),true) +$(build)/compile-x86-asm.o: $(src)/continuations-x86.$(asm-format) +endif + +$(build)/run-tests.sh: $(test-classes) makefile + echo 'cd $$(dirname $$0)' > $(@) + echo "sh ./test.sh 2>/dev/null \\" >> $(@) + echo "$(shell echo $(library-path) | sed 's|$(build)|\.|g') ./$(name)-unittest${exe-suffix} ./$(notdir $(test-executable)) $(mode) \"-Djava.library.path=. -cp test\" \\" >> $(@) + echo "$(call class-names,$(test-build),$(filter-out $(test-support-classes), $(test-classes))) \\" >> $(@) + echo "$(continuation-tests) $(tail-tests)" >> $(@) + +$(build)/test.sh: $(test)/test.sh + cp $(<) $(@) gen-arg = $(shell echo $(1) | sed -e 's:$(build)/type-\(.*\)\.cpp:\1:') $(generated-code): %.cpp: $(src)/types.def $(generator) $(classpath-dep) @@ -936,14 +1451,43 @@ $(generated-code): %.cpp: $(src)/types.def $(generator) $(classpath-dep) $(classpath-build)/%.class: $(classpath-src)/%.java @echo $(<) -$(classpath-dep): $(classpath-sources) +$(classpath-dep): $(classpath-sources) $(classpath-jar-dep) @echo "compiling classpath classes" @mkdir -p $(classpath-build) - $(javac) -d $(classpath-build) -bootclasspath $(boot-classpath) \ - $(shell $(MAKE) -s --no-print-directory build=$(build) \ - $(classpath-classes)) + classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \ + $(classpath-classes))"; if [ -n "$${classes}" ]; then \ + $(javac) -d $(classpath-build) -bootclasspath $(boot-classpath) \ + $${classes}; fi @touch $(@) +$(build)/android-src/%.cpp: $(luni-native)/%.cpp + cp $(<) $(@) + +$(build)/%.o: $(build)/android-src/%.cpp $(build)/android.dep + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(android-cflags) $(classpath-extra-cflags) -c \ + $$($(windows-path) $(<)) $(call output,$(@)) + +$(build)/android.dep: $(luni-javas) $(dalvik-javas) $(xml-javas) + @echo "compiling luni classes" + @mkdir -p $(classpath-build) + @mkdir -p $(build)/android + @mkdir -p $(build)/android-src/external/fdlibm + @mkdir -p $(build)/android-src/libexpat + cp $(android)/external/fdlibm/fdlibm.h $(build)/android-src/external/fdlibm/ + cp $(android)/external/expat/lib/expat*.h $(build)/android-src/libexpat/ + cp -a $(luni-java)/* $(dalvik-java)/* $(xml-java)/* $(build)/android-src/ + sed -i -e 's/return ordinal - o.ordinal;/return ordinal - o.ordinal();/' \ + $(build)/android-src/java/lang/Enum.java + find $(build)/android-src -name '*.java' > $(build)/android.txt + $(javac) -Xmaxerrs 1000 -d $(build)/android -sourcepath $(luni-java) \ + @$(build)/android.txt + rm $(build)/android/sun/misc/Unsafe* \ + $(build)/android/java/lang/reflect/Proxy* + cp -r $(build)/android/* $(classpath-build) + @touch $(@) + $(test-build)/%.class: $(test)/%.java @echo $(<) @@ -976,31 +1520,96 @@ endef define compile-asm-object @echo "compiling $(@)" @mkdir -p $(dir $(@)) - $(as) -I$(src) $(asmflags) -c $(<) -o $(@) + $(as) $(asmflags) $(call asm-output,$(@)) $(call asm-input,$(<)) +endef + +define compile-unittest-object + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $$($(windows-path) $(<)) -I$(unittest) $(call output,$(@)) endef $(vm-cpp-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) $(compile-object) +ifeq ($(process),interpret) +$(all-codegen-target-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) +endif + +$(unittest-objects): $(build)/unittest/%.o: $(unittest)/%.cpp $(vm-depends) $(unittest-depends) + $(compile-unittest-object) + +$(build)/tools/audit-codegen/main.o: $(build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + $(test-cpp-objects): $(test-build)/%.o: $(test)/%.cpp $(vm-depends) $(compile-object) $(test-library): $(test-cpp-objects) @echo "linking $(@)" -ifdef msvc - $(ld) $(shared) $(lflags) $(^) -out:$(@) -PDB:$(@).pdb \ - -IMPLIB:$(test-build)/$(name).lib -MANIFESTFILE:$(@).manifest - $(mt) -manifest $(@).manifest -outputresource:"$(@);2" +ifdef ms_cl_compiler + $(ld) $(shared) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(so-suffix),.pdb,$(@)) \ + -IMPLIB:$(test-build)/$(name).lib $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);2" +endif else $(ld) $(^) $(shared) $(lflags) -o $(@) endif +ifdef embed +$(embed): $(embed-objects) $(embed-loader-o) + @echo "building $(embed)" +ifdef ms_cl_compiler + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(cxx) $(^) $(lflags) $(static) $(call output,$(@)) +endif + +$(build-embed)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $(<) $(call output,$(@)) + +$(embed-loader-o): $(embed-loader) $(converter) + @mkdir -p $(dir $(@)) + $(converter) $(<) $(@) _binary_loader_start \ + _binary_loader_end $(target-format) $(arch) + +$(embed-loader): $(embed-loader-objects) $(vm-objects) $(classpath-objects) \ + $(heapwalk-objects) $(lzma-decode-objects) +ifdef ms_cl_compiler + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif +else + $(dlltool) -z $(addsuffix .def,$(basename $(@))) $(^) + $(dlltool) -d $(addsuffix .def,$(basename $(@))) -e $(addsuffix .exp,$(basename $(@))) + $(ld) $(addsuffix .exp,$(basename $(@))) $(^) \ + $(lflags) $(bootimage-lflags) -o $(@) +endif + $(strip) $(strip-all) $(@) + +$(build-embed-loader)/%.o: $(src)/%.cpp + @echo "compiling $(@)" + @mkdir -p $(dir $(@)) + $(cxx) $(cflags) -c $(<) $(call output,$(@)) +endif + $(build)/%.o: $(lzma)/C/%.c @echo "compiling $(@)" @mkdir -p $(dir $(@)) $(cxx) $(cflags) $(no-error) -c $$($(windows-path) $(<)) $(call output,$(@)) -$(vm-asm-objects): $(build)/%-asm.o: $(src)/%.S +$(vm-asm-objects): $(build)/%-asm.o: $(src)/%.$(asm-format) $(compile-asm-object) $(bootimage-generator-objects): $(build)/%.o: $(src)/%.cpp $(vm-depends) @@ -1024,11 +1633,12 @@ $(boot-object): $(boot-source) $(boot-javahome-object): $(src)/boot-javahome.cpp $(compile-object) -$(converter-objects) $(converter-tool-objects): $(build)/binaryToObject/%.o: $(src)/binaryToObject/%.cpp $(converter-depends) +$(object-writer-objects) $(binary-to-object-objects): $(build)/%.o: $(src)/%.cpp $(binary-to-object-depends) $(object-writer-depends) $(all-depends) @mkdir -p $(dir $(@)) $(build-cxx) $(converter-cflags) -c $(<) -o $(@) $(converter): $(converter-objects) $(converter-tool-objects) + @mkdir -p $(dir $(@)) $(build-cc) $(^) -g -o $(@) $(lzma-encoder-objects): $(build)/lzma/%.o: $(src)/lzma/%.cpp @@ -1086,40 +1696,92 @@ $(jni-objects): $(build)/%.o: $(classpath-src)/%.cpp $(static-library): $(vm-objects) $(classpath-objects) $(vm-heapwalk-objects) \ $(javahome-object) $(boot-javahome-object) $(lzma-decode-objects) @echo "creating $(@)" + @rm -rf $(build)/libavian + @mkdir -p $(build)/libavian rm -rf $(@) - $(ar) cru $(@) $(^) + for x in $(^); \ + do cp $${x} $(build)/libavian/$$(echo $${x} | sed s:/:_:g); \ + done +ifdef ms_cl_compiler + $(ar) $(arflags) $(build)/libavian/*.o -out:$(@) +else + $(ar) cru $(@) $(build)/libavian/*.o $(ranlib) $(@) +endif -$(bootimage-object) $(codeimage-object): $(bootimage-generator) +$(bootimage-object) $(codeimage-object): $(bootimage-generator) \ + $(classpath-jar-dep) + @echo "generating bootimage and codeimage binaries from $(classpath-build) using $(<)" $(<) -cp $(classpath-build) -bootimage $(bootimage-object) -codeimage $(codeimage-object) \ - -bootimage-symbols _binary_bootimage_bin_start:_binary_bootimage_bin_end \ - -codeimage-symbols _binary_codeimage_bin_start:_binary_codeimage_bin_end + -bootimage-symbols $(bootimage-symbols) \ + -codeimage-symbols $(codeimage-symbols) executable-objects = $(vm-objects) $(classpath-objects) $(driver-object) \ $(vm-heapwalk-objects) $(boot-object) $(vm-classpath-objects) \ $(javahome-object) $(boot-javahome-object) $(lzma-decode-objects) -$(executable): $(executable-objects) - @echo "linking $(@)" -ifeq ($(platform),windows) -ifdef msvc - $(ld) $(lflags) $(executable-objects) -out:$(@) -PDB:$(@).pdb \ - -IMPLIB:$(@).lib -MANIFESTFILE:$(@).manifest - $(mt) -manifest $(@).manifest -outputresource:"$(@);1" -else - $(dlltool) -z $(@).def $(executable-objects) - $(dlltool) -d $(@).def -e $(@).exp - $(ld) $(@).exp $(executable-objects) $(lflags) -o $(@) -endif -else - $(ld) $(executable-objects) $(rdynamic) $(lflags) $(bootimage-lflags) -o $(@) -endif - $(strip) $(strip-all) $(@) +unittest-executable-objects = $(unittest-objects) $(vm-objects) \ + $(build)/util/arg-parser.o $(stub-objects) -$(bootimage-generator): $(bootimage-generator-objects) - echo arch=$(arch) platform=$(platform) +ifeq ($(process),interpret) + unittest-executable-objects += $(all-codegen-target-objects) +endif + +audit-codegen-objects = $(call cpp-objects,$(audit-codegen-sources),$(src),$(build)) +audit-codegen-executable-objects = $(audit-codegen-objects) $(vm-objects) $(build)/util/arg-parser.o + +.PHONY: print +print: + @echo $(audit-codegen-objects) + +# apparently, make does poorly with ifs inside of defines, and indented defines. +# I suggest re-indenting the following before making edits (and unindenting afterwards): +ifneq ($(platform),windows) +define link-executable + @echo linking $(@) + $(ld) $(^) $(rdynamic) $(lflags) $(classpath-lflags) $(bootimage-lflags) \ + -o $(@) +endef +else +ifdef ms_cl_compiler +ifdef mt +define link-executable + @echo linking $(@) + $(ld) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endef +else +define link-executable + @echo linking $(@) + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endef +endif +else +define link-executable + @echo linking $(@) + $(dlltool) -z $(@).def $(^) + $(dlltool) -d $(@).def -e $(@).exp + $(ld) $(@).exp $(^) $(lflags) $(classpath-lflags) -o $(@) +endef +endif +endif + +$(executable): $(executable-objects) + $(link-executable) + +$(unittest-executable): $(unittest-executable-objects) + $(link-executable) + +$(audit-codegen-executable): $(audit-codegen-executable-objects) + $(link-executable) + +$(bootimage-generator): $(bootimage-generator-objects) $(vm-objects) + echo building $(bootimage-generator) arch=$(build-arch) platform=$(bootimage-platform) $(MAKE) mode=$(mode) \ + build=$(host-build-root) \ arch=$(build-arch) \ + aot-only=false \ target-arch=$(arch) \ platform=$(bootimage-platform) \ target-format=$(target-format) \ @@ -1132,22 +1794,24 @@ $(bootimage-generator): $(bootimage-generator-objects) $(bootimage-generator) $(build-bootimage-generator): \ - $(vm-objects) $(classpath-object) $(classpath-objects) \ + $(vm-objects) $(classpath-object) \ $(heapwalk-objects) $(bootimage-generator-objects) $(converter-objects) \ $(lzma-decode-objects) $(lzma-encode-objects) @echo "linking $(@)" ifeq ($(platform),windows) -ifdef msvc - $(ld) $(lflags) $(^) -out:$(@) -PDB:$(@).pdb -IMPLIB:$(@).lib \ - -MANIFESTFILE:$(@).manifest - $(mt) -manifest $(@).manifest -outputresource:"$(@);1" +ifdef ms_cl_compiler + $(ld) $(bootimage-generator-lflags) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif else $(dlltool) -z $(@).def $(^) $(dlltool) -d $(@).def -e $(@).exp - $(ld) $(@).exp $(^) $(lflags) -o $(@) + $(ld) $(@).exp $(^) $(bootimage-generator-lflags) $(lflags) -o $(@) endif else - $(ld) $(^) $(rdynamic) $(lflags) -o $(@) + $(ld) $(^) $(rdynamic) $(bootimage-generator-lflags) $(lflags) -o $(@) endif $(dynamic-library): $(vm-objects) $(dynamic-object) $(classpath-objects) \ @@ -1155,12 +1819,16 @@ $(dynamic-library): $(vm-objects) $(dynamic-object) $(classpath-objects) \ $(classpath-libraries) $(javahome-object) $(boot-javahome-object) \ $(lzma-decode-objects) @echo "linking $(@)" -ifdef msvc - $(ld) $(shared) $(lflags) $(^) -out:$(@) -PDB:$(@).pdb \ - -IMPLIB:$(build)/$(name).lib -MANIFESTFILE:$(@).manifest - $(mt) -manifest $(@).manifest -outputresource:"$(@);2" +ifdef ms_cl_compiler + $(ld) $(shared) $(lflags) $(^) -out:$(@) \ + -debug -PDB:$(subst $(so-suffix),.pdb,$(@)) \ + -IMPLIB:$(subst $(so-suffix),.lib,$(@)) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);2" +endif else - $(ld) $(^) $(version-script-flag) $(shared) $(lflags) $(bootimage-lflags) \ + $(ld) $(^) $(version-script-flag) $(soname-flag) \ + $(shared) $(lflags) $(classpath-lflags) $(bootimage-lflags) \ -o $(@) endif $(strip) $(strip-all) $(@) @@ -1169,13 +1837,15 @@ endif # Ubuntu 11.10 which may be fixable without disabling LTO. $(executable-dynamic): $(driver-dynamic-objects) $(dynamic-library) @echo "linking $(@)" -ifdef msvc +ifdef ms_cl_compiler $(ld) $(lflags) -LIBPATH:$(build) -DEFAULTLIB:$(name) \ - -PDB:$(@).pdb -IMPLIB:$(@).lib $(driver-dynamic-objects) -out:$(@) \ - -MANIFESTFILE:$(@).manifest - $(mt) -manifest $(@).manifest -outputresource:"$(@);1" + -debug -PDB:$(subst $(exe-suffix),.pdb,$(@)) \ + $(driver-dynamic-objects) -out:$(@) $(manifest-flags) +ifdef mt + $(mt) -nologo -manifest $(@).manifest -outputresource:"$(@);1" +endif else - $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) $(no-lto) -o $(@) + $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) $(no-lto) $(rpath) -o $(@) endif $(strip) $(strip-all) $(@) diff --git a/openjdk-src.mk b/openjdk-src.mk index fbbce2e5e8..0f59e03bcb 100644 --- a/openjdk-src.mk +++ b/openjdk-src.mk @@ -196,6 +196,8 @@ ifeq ($(platform),windows) $(openjdk-src)/windows/native/java/net/NetworkInterface_winXP.c \ $(openjdk-src)/windows/native/java/net/SocketInputStream.c \ $(openjdk-src)/windows/native/java/net/SocketOutputStream.c \ + $(openjdk-src)/windows/native/java/net/TwoStacksPlainDatagramSocketImpl.c \ + $(openjdk-src)/windows/native/java/net/TwoStacksPlainSocketImpl.c \ $(openjdk-src)/windows/native/java/util/WindowsPreferences.c \ $(openjdk-src)/windows/native/java/util/logging.c \ $(openjdk-src)/windows/native/java/util/TimeZone_md.c \ @@ -216,6 +218,9 @@ ifeq ($(platform),windows) openjdk-headers-classes += \ java.net.DualStackPlainSocketImpl \ + java.net.SocketImpl \ + java.net.TwoStacksPlainDatagramSocketImpl \ + java.net.TwoStacksPlainSocketImpl \ java.lang.ProcessImpl \ sun.io.Win32ErrorMode \ sun.nio.ch.WindowsSelectorImpl \ @@ -327,7 +332,8 @@ else $(src)/openjdk/my_java_props_macosx.c else openjdk-sources += \ - $(openjdk-src)/solaris/native/java/lang/java_props_macosx.c + $(openjdk-src)/solaris/native/java/lang/java_props_macosx.c \ + $(openjdk-src)/macosx/native/sun/nio/ch/KQueueArrayWrapper.c endif openjdk-cflags += \ diff --git a/openjdk.pro b/openjdk.pro index 8207016de5..4ad548281d 100644 --- a/openjdk.pro +++ b/openjdk.pro @@ -14,6 +14,10 @@ public static void setProperties(java.util.Properties); } +-keep class sun.misc.Launcher { + public static sun.misc.Launcher getLauncher(); + } + -keep class java.lang.ClassLoader { private static java.lang.ClassLoader scl; private static boolean sclSet; @@ -42,7 +46,7 @@ } -keep class avian.OpenJDK { - public static java.security.ProtectionDomain getProtectionDomain(); + ; } -keepclassmembers public class java.security.PrivilegedAction { @@ -132,11 +136,30 @@ public InetSocketAddress(java.net.InetAddress, int); } -keep class java.net.ServerSocket +-keep class java.net.SocketTimeoutException -keepclassmembers class java.net.PlainSocketImpl { ; } +-keepclassmembers class java.net.TwoStacksPlainSocketImpl { + *** fd1; + *** lastfd; +} + +-keepclassmembers class java.net.AbstractPlainSocketImpl { + *** timeout; + *** trafficClass; +} + +-keepclassmembers class java.net.SocketImpl { + *** serverSocket; + *** fd; + *** address; + *** port; + *** localport; +} + -keepclassmembers class java.io.FileInputStream { private java.io.FileDescriptor fd; } @@ -208,7 +231,7 @@ -keep class sun.nio.cs.UTF_8 # loaded reflectively to handle embedded resources: --keep class avian.resource.Handler +-keep class avian.avianvmresource.Handler # refered to symbolically in MethodAccessorGenerator: -keep class sun.reflect.MethodAccessorImpl { @@ -244,3 +267,41 @@ -keep class sun.nio.fs.UnixException { UnixException(int); } + +-keep class sun.net.www.protocol.jar.Handler + +# These concurrent classes refer to certain members reflectively in their static initializers +-keepclassmembers class java.util.concurrent.ConcurrentHashMap$HashEntry { + *** next; +} + +-keepclassmembers class java.util.concurrent.CopyOnWriteArrayList { + *** lock; +} + +-keepclassmembers class java.util.concurrent.CountDownLatch { + *** allocationSpinLock; +} + +-keepclassmembers class java.util.concurrent.PriorityBlockingQueue { + *** allocationSpinLock; +} + +-keepclassmembers class java.util.concurrent.SynchronousQueue$TransferStack { + *** head; +} + +-keepclassmembers class java.util.concurrent.ConcurrentLinkedQueue { + *** head; + *** tail; +} + +-keepclassmembers class java.util.concurrent.ConcurrentLinkedQueue$Node { + *** item; + *** next; +} + +-keepclassmembers class java.util.concurrent.SynchronousQueue$TransferStack$SNode { + *** match; + *** next; +} diff --git a/readme.txt b/readme.txt deleted file mode 100644 index e9185b2c85..0000000000 --- a/readme.txt +++ /dev/null @@ -1,727 +0,0 @@ -Quick Start ------------ - -on Linux: - $ export JAVA_HOME=/usr/local/java # or wherever you have the JDK installed - $ make - $ build/linux-i386/avian -cp build/linux-i386/test Hello - -on Mac OS X: - $ export JAVA_HOME=/Library/Java/Home - $ make - $ build/darwin-i386/avian -cp build/darwin-i386/test Hello - -on Windows (MSYS): - $ git clone git://oss.readytalk.com/win32.git ../win32 - $ export JAVA_HOME="C:/Program Files/Java/jdk1.6.0_07" - $ make - $ build/windows-i386/avian -cp build/windows-i386/test Hello - -on Windows (Cygwin): - $ git clone git://oss.readytalk.com/win32.git ../win32 - $ export JAVA_HOME="/cygdrive/c/Program Files/Java/jdk1.6.0_07" - $ make - $ build/windows-i386/avian -cp build/windows-i386/test Hello - -Adjust JAVA_HOME according to your system, but be sure to use forward -slashes in the path. - - -Introduction ------------- - -Avian is a lightweight virtual machine and class library designed to -provide a useful subset of Java's features, suitable for building -self-contained applications. More information is available at the -project web site: - - http://oss.readytalk.com/avian - -If you have any trouble building, running, or embedding Avian, please -post a message to our discussion group: - - http://groups.google.com/group/avian - -That's also the place for any other questions, comments, or -suggestions you might have. - - -Supported Platforms -------------------- - -Avian can currently target the following platforms: - - Linux (i386, x86_64, ARM, and 32-bit PowerPC) - Windows (i386 and x86_64) - Mac OS X (i386, x86_64 and 32-bit PowerPC) - Apple iOS (i386 and ARM) - -Building --------- - -Build requirements include: - - * GNU make 3.80 or later - * GCC 3.4 or later (4.5.1 or later for Windows/x86_64) - or LLVM Clang 3.1 or later (see use-clang option below) - * JDK 1.5 or later - * MinGW 3.4 or later (only if compiling for Windows) - * zlib 1.2.3 or later - -Earlier versions of some of these packages may also work but have not -been tested. - -The build is directed by a single makefile and may be influenced via -certain flags described below, all of which are optional. - - $ make \ - platform={linux,windows,darwin} \ - arch={i386,x86_64,powerpc,arm} \ - process={compile,interpret} \ - mode={debug,debug-fast,fast,small} \ - lzma= \ - ios={true,false} \ - bootimage={true,false} \ - heapdump={true,false} \ - tails={true,false} \ - continuations={true,false} \ - use-clang={true,false} \ - openjdk= \ - openjdk-src= - - * platform - the target platform - default: output of $(uname -s | tr [:upper:] [:lower:]), - normalized in some cases (e.g. CYGWIN_NT-5.1 -> windows) - - * arch - the target architecture - default: output of $(uname -m), normalized in some cases - (e.g. i686 -> i386) - - * process - choice between pure interpreter or JIT compiler - default: compile - - * mode - which set of compilation flags to use to determine - optimization level, debug symbols, and whether to enable - assertions - default: fast - - * lzma - if set, support use of LZMA to compress embedded JARs and - boot images. The value of this option should be a directory - containing a recent LZMA SDK (available at - http://www.7-zip.org/sdk.html). Currently, only version 9.20 of - the SDK has been tested, but other versions might work. - default: not set - - * ios - if true, cross-compile for iOS on OS X. Note that - non-jailbroken iOS devices do not allow JIT compilation, so only - process=interpret or bootimage=true builds will run on such - devices. See https://github.com/ReadyTalk/hello-ios for an - example of an Xcode project for iOS which uses Avian. - default: false - - * bootimage - if true, create a boot image containing the pre-parsed - class library and ahead-of-time compiled methods. This option is - only valid for process=compile builds. Note that you may need to - specify both build-arch=x86_64 and arch=x86_64 on 64-bit systems - where "uname -m" prints "i386". - default: false - - * heapdump - if true, implement avian.Machine.dumpHeap(String), - which, when called, will generate a snapshot of the heap in a - simple, ad-hoc format for memory profiling purposes. See - 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 - - * use-clang - if true, use LLVM's clang instead of GCC to build. - Note that this does not currently affect cross compiles, only - native builds. - default: false - - * openjdk - if set, use OpenJDK class library instead of the default - Avian class library. See "Building with the OpenJDK Class - Library" below for details. - default: not set - - * openjdk-src - if this and the openjdk option above are both set, - build an embeddable VM using the OpenJDK class library. The JNI - components of the OpenJDK class library will be built from the - sources found under the specified directory. See "Building with - the OpenJDK Class Library" below for details. - default: not set - -These flags determine the name of the directory used for the build. -The name always starts with ${platform}-${arch}, and each non-default -build option is appended to the name. For example, a debug build with -bootimage enabled on Linux/i386 would be built in -build/linux-i386-debug-bootimage. This allows you to build with -several different sets of options independently and even -simultaneously without doing a clean build each time. - -If you are compiling for Windows, you may either cross-compile using -MinGW or build natively on Windows under MSYS or Cygwin. - -Installing MSYS: - - 1. Download and install the current MinGW and MSYS packages from - mingw.org, selecting the C and C++ compilers when prompted. Use the - post-install script to create the filesystem link to the compiler. - - 2. Download GNU Make 3.81 from the MSYS download page - (make-3.81-MSYS-1.0.11-2.tar.bz2) and extract the tar file into - e.g. c:/msys/1.0. - -Installing Cygwin: - - 1. Download and run setup.exe from cygwin.com, installing the base - system and these packages: make, gcc-mingw-g++, - mingw64-i686-gcc-g++, mingw64-x86_64-gcc-g++, and (optionally) git. - -You may also find our win32 repository useful: (run this from the -directory containing the avian directory) - - $ git clone git://oss.readytalk.com/win32.git - -This gives you the Windows JNI headers, zlib headers and library, and -a few other useful libraries like OpenSSL, libjpeg, and libpng. -There's also a win64 repository for 64-bit builds: - - $ git clone git://oss.readytalk.com/win64.git - - -Building with the Microsoft Visual C++ Compiler ------------------------------------------------ - -You can also build using the MSVC compiler, which makes debugging with -tools like WinDbg and Visual Studio much easier. Note that you will -still need to have GCC installed - MSVC is only used to compile the -C++ portions of the VM, while the assembly code and helper tools are -built using GCC. - -The MSVC build has been tested with Visual Studio Express Edition -versions 8, 9, and 10. Other versions may also work. - -To build with MSVC, install Cygwin as described above and set the -following environment variables: - - $ export PATH="/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/BIN:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/Common7/Tools:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v3.5:/cygdrive/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727:/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC/VCPackages:/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.0A/bin:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS:/cygdrive/c/WINDOWS/System32/Wbem" - - $ export LIBPATH="C:\WINDOWS\Microsoft.NET\Framework\v3.5;C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;" - - $ export VCINSTALLDIR="C:\Program Files\Microsoft Visual Studio 9.0\VC" - - $ export LIB="C:\Program Files\Microsoft Visual Studio 9.0\VC\LIB;C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib;" - - $ export INCLUDE="C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE;C:\Program Files\Microsoft SDKs\Windows\v6.0A\include;" - -Adjust these definitions as necessary according to your MSVC -installation. - -Finally, build with the msvc flag set to the MSVC tool directory: - - $ make msvc="/cygdrive/c/Program Files/Microsoft Visual Studio 9.0/VC" - - -Building with the OpenJDK Class Library ---------------------------------------- - -By default, Avian uses its own lightweight class library. However, -that library only contains a relatively small subset of the classes -and methods included in the JRE. If your application requires -features beyond that subset, you may want to tell Avian to use -OpenJDK's class library instead. To do so, specify the directory -where OpenJDK is installed, e.g.: - - $ make openjdk=/usr/lib/jvm/java-7-openjdk - -This will build Avian as a conventional JVM (e.g. libjvm.so) which -loads its boot class library and native libraries (e.g. libjava.so) -from /usr/lib/jvm/java-7-openjdk/jre at runtime. To run an -application in this configuration, you'll need to make sure the VM is -in your library search path. For example: - - $ LD_LIBRARY_PATH=build/linux-x86_64-openjdk \ - build/linux-x86_64-openjdk/avian-dynamic -cp /path/to/my/application \ - com.example.MyApplication - -Alternatively, you can enable a stand-alone build using OpenJDK by -specifying the location of the OpenJDK source code, e.g.: - - $ make openjdk=$(pwd)/../jdk7/build/linux-amd64/j2sdk-image \ - openjdk-src=$(pwd)/../jdk7/jdk/src - -You must ensure that the path specified for openjdk-src does not have -any spaces in it; make gets confused when dependency paths include -spaces, and we haven't found away around that except to avoid paths -with spaces entirely. - -The result of such a build is a self-contained binary which does not -depend on external libraries, jars, or other files. In this case, the -specified paths are used only at build time; anything needed at -runtime is embedded in the binary. Thus, the process of running an -application is simplified: - - $ build/linux-x86_64-openjdk-src/avian -cp /path/to/my/application \ - com.example.MyApplication - -Note that the resulting binary will be very large due to the size of -OpenJDK's class library. This can be mitigated using UPX, preferably -an LZMA-enabled version: - - $ upx --lzma --best build/linux-x86_64-openjdk-src/avian - -You can reduce the size futher for embedded builds by using ProGuard -and the supplied openjdk.pro configuration file (see "Embedding with -ProGuard and a Boot Image" below). Note that you'll still need to use -vm.pro in that case -- openjdk.pro just adds additional constraints -specific to the OpenJDK port. Also see app.mk in -git://oss.readytalk.com/avian-swt-examples.git for an example of using -Avian, OpenJDK, ProGuard, and UPX in concert. - -Here are some examples of how to install OpenJDK and build Avian with -it on various OSes: - - Debian-based Linux: - # conventional build: - apt-get install openjdk-7-jdk - make openjdk=/usr/lib/jvm/java-7-openjdk test - - # stand-alone build: - apt-get install openjdk-7-jdk - apt-get source openjdk-7-jdk - apt-get build-dep openjdk-7-jdk - (cd openjdk-7-7~b147-2.0 && dpkg-buildpackage) - make openjdk=/usr/lib/jvm/java-7-openjdk \ - openjdk-src=$(pwd)/openjdk-7-7~b147-2.0/build/openjdk/jdk/src \ - test - - Mac OS X: - # Prerequisite: build OpenJDK 7 according to - # https://wikis.oracle.com/display/OpenJDK/Mac+OS+X+Port - - # conventional build: - make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image test - - # stand-alone build: - make openjdk=$(pwd)/../jdk7u-dev/build/macosx-amd64/j2sdk-image \ - openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test - - Windows (Cygwin): - # Prerequisite: build OpenJDK 7 according to - # http://weblogs.java.net/blog/simonis/archive/2011/10/28/yaojowbi-yet-another-openjdk-windows-build-instruction - - # conventional build: - make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image test - - # stand-alone build: - make openjdk=$(pwd)/../jdk7u-dev/build/windows-i586/j2sdk-image \ - openjdk-src=$(pwd)/../p/jdk7u-dev/jdk/src test - -Currently, only OpenJDK 7 is supported. Later versions might work, -but have not yet been tested. - - -Installing ----------- - -Installing Avian is as simple as copying the executable to the desired -directory: - - $ cp build/${platform}-${arch}/avian ~/bin/ - - -Embedding ---------- - -The following series of commands illustrates how to produce a -stand-alone executable out of a Java application using Avian. - -Note: if you are building on Cygwin, prepend "x86_64-w64-mingw32-" or -"i686-w64-mingw32-" to the ar, g++, gcc, strip, and dlltool commands -below (e.g. x86_64-w64-mingw32-gcc). - -Step 1: Build Avian, create a new directory, and populate it with the -VM object files and bootstrap classpath jar. - - $ make - $ mkdir hello - $ cd hello - $ ar x ../build/${platform}-${arch}/libavian.a - $ cp ../build/${platform}-${arch}/classpath.jar boot.jar - -Step 2: Build the Java code and add it to the jar. - - $ cat >Hello.java <embedded-jar-main.cpp <("-Xbootclasspath:[bootJar]"); - - JavaVM* vm; - void* env; - JNI_CreateJavaVM(&vm, &env, &vmArgs); - JNIEnv* e = static_cast(env); - - jclass c = e->FindClass("Hello"); - if (not e->ExceptionCheck()) { - jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); - if (not e->ExceptionCheck()) { - jclass stringClass = e->FindClass("java/lang/String"); - if (not e->ExceptionCheck()) { - jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); - if (not e->ExceptionCheck()) { - for (int i = 1; i < ac; ++i) { - e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); - } - - e->CallStaticVoidMethod(c, m, a); - } - } - } - } - - int exitCode = 0; - if (e->ExceptionCheck()) { - exitCode = -1; - e->ExceptionDescribe(); - } - - vm->DestroyJavaVM(); - - return exitCode; -} -EOF - -on Linux: - $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ - -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o - -on Mac OS X: - $ g++ -I$JAVA_HOME/include -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp \ - -o main.o - -on Windows: - $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 \ - -D_JNI_IMPLEMENTATION_ -c embedded-jar-main.cpp -o main.o - -Step 5: Link the objects produced above to produce the final -executable, and optionally strip its symbols. - -on Linux: - $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello - $ strip --strip-all hello - -on Mac OS X: - $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello -framework CoreFoundation - $ strip -S -x hello - -on Windows: - $ dlltool -z hello.def *.o - $ dlltool -d hello.def -e hello.exp - $ g++ hello.exp *.o -L../../win32/lib -lmingwthrd -lm -lz -lws2_32 \ - -mwindows -mconsole -o hello.exe - $ strip --strip-all hello.exe - - -Embedding with ProGuard and a Boot Image ----------------------------------------- - -The following illustrates how to embed an application as above, except -this time we preprocess the code using ProGuard and build a boot image -from it for quicker startup. The pros and cons of using ProGuard are -as follow: - - * Pros: ProGuard will eliminate unused code, optimize the rest, and - obfuscate it as well for maximum space savings - - * Cons: increased build time, especially for large applications, and - extra effort needed to configure it for applications which rely - heavily on reflection and/or calls to Java from native code - -For boot image builds: - - * Pros: the boot image build pre-parses all the classes and compiles - all the methods, obviating the need for JIT compilation at runtime. - This also makes garbage collection faster, since the pre-parsed - classes are never visited. - - * Cons: the pre-parsed classes and AOT-compiled methods take up more - space in the executable than the equivalent class files. In - practice, this can make the executable 30-50% larger. Also, AOT - compilation does not yet yield significantly faster or smaller code - than JIT compilation. Finally, floating point code may be slower - on 32-bit x86 since the compiler cannot assume SSE2 support will be - available at runtime, and the x87 FPU is not supported except via - out-of-line helper functions. - -Note you can use ProGuard without using a boot image and vice-versa, -as desired. - -The following instructions assume we are building for Linux/i386. -Please refer to the previous example for guidance on other platforms. - -Step 1: Build Avian, create a new directory, and populate it with the -VM object files. - - $ make bootimage=true - $ mkdir hello - $ cd hello - $ ar x ../build/linux-i386-bootimage/libavian.a - -Step 2: Create a stage1 directory and extract the contents of the -class library jar into it. - - $ mkdir stage1 - $ (cd stage1 && jar xf ../../build/linux-i386-bootimage/classpath.jar) - -Step 3: Build the Java code and add it to stage1. - - $ cat >Hello.java <hello.pro <bootimage-main.cpp <("-Davian.bootimage=bootimageBin"); - - options[1].optionString - = const_cast("-Davian.codeimage=codeimageBin"); - - JavaVM* vm; - void* env; - JNI_CreateJavaVM(&vm, &env, &vmArgs); - JNIEnv* e = static_cast(env); - - jclass c = e->FindClass("Hello"); - if (not e->ExceptionCheck()) { - jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); - if (not e->ExceptionCheck()) { - jclass stringClass = e->FindClass("java/lang/String"); - if (not e->ExceptionCheck()) { - jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); - if (not e->ExceptionCheck()) { - for (int i = 1; i < ac; ++i) { - e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); - } - - e->CallStaticVoidMethod(c, m, a); - } - } - } - } - - int exitCode = 0; - if (e->ExceptionCheck()) { - exitCode = -1; - e->ExceptionDescribe(); - } - - vm->DestroyJavaVM(); - - return exitCode; -} -EOF - - $ g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \ - -D_JNI_IMPLEMENTATION_ -c bootimage-main.cpp -o main.o - -Step 8: Link the objects produced above to produce the final -executable, and optionally strip its symbols. - - $ g++ -rdynamic *.o -ldl -lpthread -lz -o hello - $ strip --strip-all hello - - -Trademarks ----------- - -Oracle and Java are registered trademarks of Oracle and/or its -affiliates. Other names may be trademarks of their respective owners. - -The Avian project is not affiliated with Oracle. diff --git a/src/android/stubs.cpp b/src/android/stubs.cpp new file mode 100644 index 0000000000..157bdc0ad7 --- /dev/null +++ b/src/android/stubs.cpp @@ -0,0 +1,6 @@ +struct JavaVM; + +extern "C" int JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} diff --git a/src/arm.S b/src/arm.S index 01d0839079..a8c89f7ea1 100644 --- a/src/arm.S +++ b/src/arm.S @@ -9,7 +9,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" +#include "avian/types.h" .text @@ -56,16 +56,25 @@ LOCAL(loop): // setup argument registers if necessary tst r6, r6 +#if (defined __APPLE__) && (defined __clang_major__) && (__clang_major__ >= 4) + ldmiane r6, {r0-r3} +#else ldmneia r6, {r0-r3} -#if defined(__VFP_FP__) && (! defined(__SOFTFP__)) && (! defined(__QNX__)) +#endif +#if defined(__ARM_PCS_VFP) // and VFP registers vldmia r7, {d0-d7} #endif +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r4 +#else blx r4 // call function +#endif add sp, sp, r5 // deallocate stack -#if defined(__VFP_FP__) && (! defined(__SOFTFP__)) && (! defined(__QNX__)) +#if defined(__ARM_PCS_VFP) cmp r8,#FLOAT_TYPE bne LOCAL(double) fmrs r0,s0 @@ -108,7 +117,12 @@ GLOBAL(vmRun): mov r12, r0 ldr r0, [r2, #CHECKPOINT_THREAD] +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r12 +#else blx r12 +#endif .globl GLOBAL(vmRun_returnAddress) .align 2 diff --git a/src/arm.cpp b/src/arm.cpp deleted file mode 100644 index 97f9455495..0000000000 --- a/src/arm.cpp +++ /dev/null @@ -1,3013 +0,0 @@ -/* Copyright (c) 2010-2012, 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 "assembler.h" -#include "vector.h" - -#define CAST1(x) reinterpret_cast(x) -#define CAST2(x) reinterpret_cast(x) -#define CAST3(x) reinterpret_cast(x) -#define CAST_BRANCH(x) reinterpret_cast(x) - -using namespace vm; - -namespace { - -namespace isa { -// SYSTEM REGISTERS -const int FPSID = 0x0; -const int FPSCR = 0x1; -const int FPEXC = 0x8; -// INSTRUCTION OPTIONS -enum CONDITION { EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV }; -enum SHIFTOP { LSL, LSR, ASR, ROR }; -// INSTRUCTION FORMATS -inline int DATA(int cond, int opcode, int S, int Rn, int Rd, int shift, int Sh, int Rm) -{ return cond<<28 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | shift<<7 | Sh<<5 | Rm; } -inline int DATAS(int cond, int opcode, int S, int Rn, int Rd, int Rs, int Sh, int Rm) -{ return cond<<28 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | Rs<<8 | Sh<<5 | 1<<4 | Rm; } -inline int DATAI(int cond, int opcode, int S, int Rn, int Rd, int rot, int imm) -{ return cond<<28 | 1<<25 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | rot<<8 | (imm&0xff); } -inline int BRANCH(int cond, int L, int offset) -{ return cond<<28 | 5<<25 | L<<24 | (offset&0xffffff); } -inline int BRANCHX(int cond, int L, int Rm) -{ return cond<<28 | 0x4bffc<<6 | L<<5 | 1<<4 | Rm; } -inline int MULTIPLY(int cond, int mul, int S, int Rd, int Rn, int Rs, int Rm) -{ return cond<<28 | mul<<21 | S<<20 | Rd<<16 | Rn<<12 | Rs<<8 | 9<<4 | Rm; } -inline int XFER(int cond, int P, int U, int B, int W, int L, int Rn, int Rd, int shift, int Sh, int Rm) -{ return cond<<28 | 3<<25 | P<<24 | U<<23 | B<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | shift<<7 | Sh<<5 | Rm; } -inline int XFERI(int cond, int P, int U, int B, int W, int L, int Rn, int Rd, int offset) -{ return cond<<28 | 2<<25 | P<<24 | U<<23 | B<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | (offset&0xfff); } -inline int XFER2(int cond, int P, int U, int W, int L, int Rn, int Rd, int S, int H, int Rm) -{ return cond<<28 | P<<24 | U<<23 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | 1<<7 | S<<6 | H<<5 | 1<<4 | Rm; } -inline int XFER2I(int cond, int P, int U, int W, int L, int Rn, int Rd, int offsetH, int S, int H, int offsetL) -{ return cond<<28 | P<<24 | U<<23 | 1<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | offsetH<<8 | 1<<7 | S<<6 | H<<5 | 1<<4 | (offsetL&0xf); } -inline int BLOCKXFER(int cond, int P, int U, int S, int W, int L, int Rn, int rlist) -{ return cond<<28 | 4<<25 | P<<24 | U<<23 | S<<22 | W<<21 | L<<20 | Rn<<16 | rlist; } -inline int SWI(int cond, int imm) -{ return cond<<28 | 0x0f<<24 | (imm&0xffffff); } -inline int SWAP(int cond, int B, int Rn, int Rd, int Rm) -{ return cond<<28 | 1<<24 | B<<22 | Rn<<16 | Rd<<12 | 9<<4 | Rm; } -inline int COOP(int cond, int opcode_1, int CRn, int CRd, int cp_num, int opcode_2, int CRm) -{ return cond<<28 | 0xe<<24 | opcode_1<<20 | CRn<<16 | CRd<<12 | cp_num<<8 | opcode_2<<5 | CRm; } -inline int COXFER(int cond, int P, int U, int N, int W, int L, int Rn, int CRd, int cp_num, int offset) // offset is in words, not bytes -{ return cond<<28 | 0x6<<25 | P<<24 | U<<23 | N<<22 | W<<21 | L<<20 | Rn<<16 | CRd<<12 | cp_num<<8 | (offset&0xff)>>2; } -inline int COREG(int cond, int opcode_1, int L, int CRn, int Rd, int cp_num, int opcode_2, int CRm) -{ return cond<<28 | 0xe<<24 | opcode_1<<21 | L<<20 | CRn<<16 | Rd<<12 | cp_num<<8 | opcode_2<<5 | 1<<4 | CRm; } -inline int COREG2(int cond, int L, int Rn, int Rd, int cp_num, int opcode, int CRm) -{ return cond<<28 | 0xc4<<20 | L<<20 | Rn<<16 | Rd<<12 | cp_num<<8 | opcode<<4 | CRm;} -// FIELD CALCULATORS -inline int calcU(int imm) { return imm >= 0 ? 1 : 0; } -// INSTRUCTIONS -// The "cond" and "S" fields are set using the SETCOND() and SETS() functions -inline int b(int offset) { return BRANCH(AL, 0, offset); } -inline int bl(int offset) { return BRANCH(AL, 1, offset); } -inline int bx(int Rm) { return BRANCHX(AL, 0, Rm); } -inline int blx(int Rm) { return BRANCHX(AL, 1, Rm); } -inline int swi(int imm) { return SWI(AL, imm); } -inline int and_(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x0, 0, Rn, Rd, shift, Sh, Rm); } -inline int eor(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x1, 0, Rn, Rd, shift, Sh, Rm); } -inline int sub(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x2, 0, Rn, Rd, shift, Sh, Rm); } -inline int rsb(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x3, 0, Rn, Rd, shift, Sh, Rm); } -inline int add(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x4, 0, Rn, Rd, shift, Sh, Rm); } -inline int adc(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x5, 0, Rn, Rd, shift, Sh, Rm); } -inline int sbc(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x6, 0, Rn, Rd, shift, Sh, Rm); } -inline int rsc(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x7, 0, Rn, Rd, shift, Sh, Rm); } -inline int tst(int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x8, 1, Rn, 0, shift, Sh, Rm); } -inline int teq(int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x9, 1, Rn, 0, shift, Sh, Rm); } -inline int cmp(int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xa, 1, Rn, 0, shift, Sh, Rm); } -inline int cmn(int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xb, 1, Rn, 0, shift, Sh, Rm); } -inline int orr(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xc, 0, Rn, Rd, shift, Sh, Rm); } -inline int mov(int Rd, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xd, 0, 0, Rd, shift, Sh, Rm); } -inline int bic(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xe, 0, Rn, Rd, shift, Sh, Rm); } -inline int mvn(int Rd, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xf, 0, 0, Rd, shift, Sh, Rm); } -inline int andi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x0, 0, Rn, Rd, rot, imm); } -inline int eori(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x1, 0, Rn, Rd, rot, imm); } -inline int subi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x2, 0, Rn, Rd, rot, imm); } -inline int rsbi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x3, 0, Rn, Rd, rot, imm); } -inline int addi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x4, 0, Rn, Rd, rot, imm); } -inline int adci(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x5, 0, Rn, Rd, rot, imm); } -inline int bici(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0xe, 0, Rn, Rd, rot, imm); } -inline int cmpi(int Rn, int imm, int rot=0) { return DATAI(AL, 0xa, 1, Rn, 0, rot, imm); } -inline int orri(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0xc, 0, Rn, Rd, rot, imm); } -inline int movi(int Rd, int imm, int rot=0) { return DATAI(AL, 0xd, 0, 0, Rd, rot, imm); } -inline int orrsh(int Rd, int Rn, int Rm, int Rs, int Sh) { return DATAS(AL, 0xc, 0, Rn, Rd, Rs, Sh, Rm); } -inline int movsh(int Rd, int Rm, int Rs, int Sh) { return DATAS(AL, 0xd, 0, 0, Rd, Rs, Sh, Rm); } -inline int mul(int Rd, int Rm, int Rs) { return MULTIPLY(AL, 0, 0, Rd, 0, Rs, Rm); } -inline int mla(int Rd, int Rm, int Rs, int Rn) { return MULTIPLY(AL, 1, 0, Rd, Rn, Rs, Rm); } -inline int umull(int RdLo, int RdHi, int Rm, int Rs) { return MULTIPLY(AL, 4, 0, RdHi, RdLo, Rs, Rm); } -inline int umlal(int RdLo, int RdHi, int Rm, int Rs) { return MULTIPLY(AL, 5, 0, RdHi, RdLo, Rs, Rm); } -inline int smull(int RdLo, int RdHi, int Rm, int Rs) { return MULTIPLY(AL, 6, 0, RdHi, RdLo, Rs, Rm); } -inline int smlal(int RdLo, int RdHi, int Rm, int Rs) { return MULTIPLY(AL, 7, 0, RdHi, RdLo, Rs, Rm); } -inline int ldr(int Rd, int Rn, int Rm, int W=0) { return XFER(AL, 1, 1, 0, W, 1, Rn, Rd, 0, 0, Rm); } -inline int ldri(int Rd, int Rn, int imm, int W=0) { return XFERI(AL, 1, calcU(imm), 0, W, 1, Rn, Rd, abs(imm)); } -inline int ldrb(int Rd, int Rn, int Rm) { return XFER(AL, 1, 1, 1, 0, 1, Rn, Rd, 0, 0, Rm); } -inline int ldrbi(int Rd, int Rn, int imm) { return XFERI(AL, 1, calcU(imm), 1, 0, 1, Rn, Rd, abs(imm)); } -inline int str(int Rd, int Rn, int Rm, int W=0) { return XFER(AL, 1, 1, 0, W, 0, Rn, Rd, 0, 0, Rm); } -inline int stri(int Rd, int Rn, int imm, int W=0) { return XFERI(AL, 1, calcU(imm), 0, W, 0, Rn, Rd, abs(imm)); } -inline int strb(int Rd, int Rn, int Rm) { return XFER(AL, 1, 1, 1, 0, 0, Rn, Rd, 0, 0, Rm); } -inline int strbi(int Rd, int Rn, int imm) { return XFERI(AL, 1, calcU(imm), 1, 0, 0, Rn, Rd, abs(imm)); } -inline int ldrh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 0, 1, Rm); } -inline int ldrhi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 0, 1, abs(imm)&0xf); } -inline int strh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 0, Rn, Rd, 0, 1, Rm); } -inline int strhi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 0, Rn, Rd, abs(imm)>>4 & 0xf, 0, 1, abs(imm)&0xf); } -inline int ldrsh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 1, Rm); } -inline int ldrshi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 1, 1, abs(imm)&0xf); } -inline int ldrsb(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 0, Rm); } -inline int ldrsbi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 1, 0, abs(imm)&0xf); } -inline int pop(int Rd) { return XFERI(AL, 0, 1, 0, 0, 1, 13, Rd, 4); } -inline int ldmfd(int Rn, int rlist) { return BLOCKXFER(AL, 0, 1, 0, 1, 1, Rn, rlist); } -inline int stmfd(int Rn, int rlist) { return BLOCKXFER(AL, 1, 0, 0, 1, 0, Rn, rlist); } -inline int swp(int Rd, int Rm, int Rn) { return SWAP(AL, 0, Rn, Rd, Rm); } -inline int swpb(int Rd, int Rm, int Rn) { return SWAP(AL, 1, Rn, Rd, Rm); } -// breakpoint instruction, this really has its own instruction format -inline int bkpt(int16_t immed) { return 0xe1200070 | (((unsigned)immed & 0xffff) >> 4 << 8) | (immed & 0xf); } -// COPROCESSOR INSTRUCTIONS -inline int cdp(int coproc, int opcode_1, int CRd, int CRn, int CRm, int opcode_2) { return COOP(AL, opcode_1, CRn, CRd, coproc, opcode_2, CRm); } -inline int mcr(int coproc, int opcode_1, int Rd, int CRn, int CRm, int opcode_2=0) { return COREG(AL, opcode_1, 0, CRn, Rd, coproc, opcode_2, CRm); } -inline int mcrr(int coproc, int opcode, int Rd, int Rn, int CRm) { return COREG2(AL, 0, Rn, Rd, coproc, opcode, CRm); } -inline int mrc(int coproc, int opcode_1, int Rd, int CRn, int CRm, int opcode_2=0) { return COREG(AL, opcode_1, 1, CRn, Rd, coproc, opcode_2, CRm); } -inline int mrrc(int coproc, int opcode, int Rd, int Rn, int CRm) { return COREG2(AL, 1, Rn, Rd, coproc, opcode, CRm); } -inline int ldc(int coproc, int CRd, int Rn, int offset=0, int W=0) { return COXFER(AL, 1, 1, 0, W, 1, Rn, CRd, coproc, offset); } -inline int ldcl(int coproc, int CRd, int Rn, int offset=0, int W=0) { return COXFER(AL, 1, 1, 1, W, 1, Rn, CRd, coproc, offset); } -inline int stc(int coproc, int CRd, int Rn, int offset=0, int W=0) { return COXFER(AL, 1, 1, 0, W, 0, Rn, CRd, coproc, offset); } -inline int stcl(int coproc, int CRd, int Rn, int offset=0, int W=0) { return COXFER(AL, 1, 1, 1, W, 0, Rn, CRd, coproc, offset); } -// VFP FLOATING-POINT INSTRUCTIONS -inline int fmacs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } -inline int fnmacs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1)|2, Sm>>1); } -inline int fmscs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|1, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } -inline int fnmscs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|1, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1)|2, Sm>>1); } -inline int fmuls(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|2, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } -inline int fnmuls(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|2, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1)|2, Sm>>1); } -inline int fadds(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|3, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } -inline int fsubs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|3, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1)|2, Sm>>1); } -inline int fdivs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|8, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } -inline int fmacd(int Dd, int Dn, int Dm) { return COOP(AL, 0, Dn, Dd, 11, 0, Dm); } -inline int fnmacd(int Dd, int Dn, int Dm) { return COOP(AL, 0, Dn, Dd, 11, 2, Dm); } -inline int fmscd(int Dd, int Dn, int Dm) { return COOP(AL, 1, Dn, Dd, 11, 0, Dm); } -inline int fnmscd(int Dd, int Dn, int Dm) { return COOP(AL, 1, Dn, Dd, 11, 2, Dm); } -inline int fmuld(int Dd, int Dn, int Dm) { return COOP(AL, 2, Dn, Dd, 11, 0, Dm); } -inline int fnmuld(int Dd, int Dn, int Dm) { return COOP(AL, 2, Dn, Dd, 11, 2, Dm); } -inline int faddd(int Dd, int Dn, int Dm) { return COOP(AL, 3, Dn, Dd, 11, 0, Dm); } -inline int fsubd(int Dd, int Dn, int Dm) { return COOP(AL, 3, Dn, Dd, 11, 2, Dm); } -inline int fdivd(int Dd, int Dn, int Dm) { return COOP(AL, 8, Dn, Dd, 11, 0, Dm); } -inline int fcpys(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int fabss(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int fnegs(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 1, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int fsqrts(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 1, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int fcmps(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 4, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int fcmpes(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 4, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int fcmpzs(int Sd) { return COOP(AL, 0xb|(Sd&1)<<2, 5, Sd>>1, 10, 2, 0); } -inline int fcmpezs(int Sd) { return COOP(AL, 0xb|(Sd&1)<<2, 5, Sd>>1, 10, 6, 0); } -inline int fcvtds(int Dd, int Sm) { return COOP(AL, 0xb, 7, Dd, 10, 6|(Sm&1), Sm>>1); } -inline int fuitos(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 8, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int fsitos(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 8, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int ftouis(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xc, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int ftouizs(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xc, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int ftosis(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 10, 2|(Sm&1), Sm>>1); } -inline int ftosizs(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 10, 6|(Sm&1), Sm>>1); } -inline int fcpyd(int Dd, int Dm) { return COOP(AL, 0xb, 0, Dd, 11, 2, Dm); } -inline int fabsd(int Dd, int Dm) { return COOP(AL, 0xb, 0, Dd, 11, 6, Dm); } -inline int fnegd(int Dd, int Dm) { return COOP(AL, 0xb, 1, Dd, 11, 2, Dm); } -inline int fsqrtd(int Dd, int Dm) { return COOP(AL, 0xb, 1, Dd, 11, 6, Dm); } -// double-precision comparison instructions -inline int fcmpd(int Dd, int Dm) { return COOP(AL, 0xb, 4, Dd, 11, 2, Dm); } -inline int fcmped(int Dd, int Dm) { return COOP(AL, 0xb, 4, Dd, 11, 6, Dm); } -inline int fcmpzd(int Dd) { return COOP(AL, 0xb, 5, Dd, 11, 2, 0); } -inline int fcmpezd(int Dd) { return COOP(AL, 0xb, 5, Dd, 11, 6, 0); } -// double-precision conversion instructions -inline int fcvtsd(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 7, Sd>>1, 11, 6, Dm); } -inline int fuitod(int Dd, int Sm) { return COOP(AL, 0xb, 8, Dd, 11, 2|(Sm&1), Sm>>1); } -inline int fsitod(int Dd, int Sm) { return COOP(AL, 0xb, 8, Dd, 11, 6|(Sm&1), Sm>>1); } -inline int ftouid(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xc, Sd>>1, 11, 2, Dm); } -inline int ftouizd(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xc, Sd>>1, 11, 6, Dm); } -inline int ftosid(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 11, 2, Dm); } -inline int ftosizd(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 11, 6, Dm); } -// these are the multiple load/store analogs for VFP, useless for now -inline int fldms(int Rn, int Sd, int count) { return COXFER(AL, 0, 1, Sd&1, 0, 1, Rn, Sd>>1, 10, count); } -inline int fldmd(int Rn, int Dd, int count) { return COXFER(AL, 0, 1, 0, 0, 1, Rn, Dd, 11, count<<1); } -inline int fldmx(int Rn, int Dd, int count) { return COXFER(AL, 0, 1, 0, 0, 1, Rn, Dd, 11, count<<1|1); } -inline int fstms(int Rn, int Sd, int count) { return COXFER(AL, 0, 1, Sd&1, 0, 0, Rn, Sd>>1, 10, count); } -inline int fstmd(int Rn, int Dd, int count) { return COXFER(AL, 0, 1, 0, 0, 0, Rn, Dd, 11, count<<1); } -inline int fstmx(int Rn, int Dd, int count) { return COXFER(AL, 0, 1, 0, 0, 0, Rn, Dd, 11, count<<1|1); } -// single load/store instructions for both precision types -inline int flds(int Sd, int Rn, int offset=0) { return COXFER(AL, 1, 1, Sd&1, 0, 1, Rn, Sd>>1, 10, offset); }; -inline int fldd(int Dd, int Rn, int offset=0) { return COXFER(AL, 1, 1, 0, 0, 1, Rn, Dd, 11, offset); }; -inline int fsts(int Sd, int Rn, int offset=0) { return COXFER(AL, 1, 1, Sd&1, 0, 0, Rn, Sd>>1, 10, offset); }; -inline int fstd(int Dd, int Rn, int offset=0) { return COXFER(AL, 1, 1, 0, 0, 0, Rn, Dd, 11, offset); }; -// move between GPRs and FPRs -inline int fmsr(int Sn, int Rd) { return mcr(10, 0, Rd, Sn>>1, 0, (Sn&1)<<2); } -inline int fmrs(int Rd, int Sn) { return mrc(10, 0, Rd, Sn>>1, 0, (Sn&1)<<2); } -/* move to/from the low/high parts of double-precision registers, - seemingly redundant */ -inline int fmdlr(int Dn, int Rd) { return mcr(11, 0, Rd, Dn, 0); } -inline int fmrdl(int Rd, int Dn) { return mrc(11, 0, Rd, Dn, 0); } -inline int fmdhr(int Dn, int Rd) { return mcr(11, 1, Rd, Dn, 0); } -inline int fmrdh(int Rd, int Dn) { return mrc(11, 1, Rd, Dn, 0); } -// move to/from VFP system registers -inline int fmxr(int reg, int Rd) { return mcr(10, 7, Rd, reg, 0); } -inline int fmrx(int Rd, int reg) { return mrc(10, 7, Rd, reg, 0); } -// these move around pairs of single-precision registers -inline int fmsrr(int Sm, int Rd, int Rn) { return mcrr(10, 1 | ((Sm&1)<<1), Rd, Rn, Sm>>1); } -inline int fmrrs(int Rd, int Rn, int Sm) { return mrrc(10, 1 | ((Sm&1)<<1), Rd, Rn, Sm>>1); } -inline int fmdrr(int Dm, int Rd, int Rn) { return mcrr(11, 1, Rd, Rn, Dm); } -inline int fmrrd(int Rd, int Rn, int Dm) { return mrrc(11, 1, Rd, Rn, Dm); } -// FLAG SETTERS -inline int SETCOND(int ins, int cond) { return ((ins&0x0fffffff) | (cond<<28)); } -inline int SETS(int ins) { return ins | 1<<20; } -// PSEUDO-INSTRUCTIONS -inline int nop() { return mov(0, 0); } -inline int lsl(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, LSL); } -inline int lsli(int Rd, int Rm, int imm) { return mov(Rd, Rm, LSL, imm); } -inline int lsr(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, LSR); } -inline int lsri(int Rd, int Rm, int imm) { return mov(Rd, Rm, LSR, imm); } -inline int asr(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, ASR); } -inline int asri(int Rd, int Rm, int imm) { return mov(Rd, Rm, ASR, imm); } -inline int ror(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, ROR); } -inline int beq(int offset) { return SETCOND(b(offset), EQ); } -inline int bne(int offset) { return SETCOND(b(offset), NE); } -inline int bls(int offset) { return SETCOND(b(offset), LS); } -inline int bhi(int offset) { return SETCOND(b(offset), HI); } -inline int blt(int offset) { return SETCOND(b(offset), LT); } -inline int bgt(int offset) { return SETCOND(b(offset), GT); } -inline int ble(int offset) { return SETCOND(b(offset), LE); } -inline int bge(int offset) { return SETCOND(b(offset), GE); } -inline int blo(int offset) { return SETCOND(b(offset), CC); } -inline int bhs(int offset) { return SETCOND(b(offset), CS); } -inline int bpl(int offset) { return SETCOND(b(offset), PL); } -inline int fmstat() { return fmrx(15, FPSCR); } -// HARDWARE FLAGS -bool vfpSupported() { - return true; // TODO -} -} - -const uint64_t MASK_LO32 = 0xffffffff; -const unsigned MASK_LO16 = 0xffff; -const unsigned MASK_LO8 = 0xff; -inline unsigned lo32(int64_t i) { return (unsigned)(i&MASK_LO32); } -inline unsigned hi32(int64_t i) { return (unsigned)(i>>32); } -inline unsigned lo16(int64_t i) { return (unsigned)(i&MASK_LO16); } -inline unsigned hi16(int64_t i) { return lo16(i>>16); } -inline unsigned lo8(int64_t i) { return (unsigned)(i&MASK_LO8); } -inline unsigned hi8(int64_t i) { return lo8(i>>8); } - -inline int ha16(int32_t i) { - return ((i >> 16) + ((i & 0x8000) ? 1 : 0)) & 0xffff; -} -inline int unha16(int32_t high, int32_t low) { - return ((high - ((low & 0x8000) ? 1 : 0)) << 16) | low; -} - -inline bool isInt8(target_intptr_t v) { return v == static_cast(v); } -inline bool isInt16(target_intptr_t v) { return v == static_cast(v); } -inline bool isInt24(target_intptr_t v) { return v == (v & 0xffffff); } -inline bool isInt32(target_intptr_t v) { return v == static_cast(v); } -inline int carry16(target_intptr_t v) { return static_cast(v) < 0 ? 1 : 0; } - -inline bool isOfWidth(int64_t i, int size) { return static_cast(i) >> size == 0; } -inline bool isOfWidth(int i, int size) { return static_cast(i) >> size == 0; } - -const int N_GPRS = 16; -const int N_FPRS = 16; -const uint32_t GPR_MASK = 0xffff; -const uint32_t FPR_MASK = 0xffff0000; -// for source-to-destination masks -const uint64_t GPR_MASK64 = GPR_MASK | (uint64_t)GPR_MASK << 32; -// making the following const somehow breaks debug symbol output in GDB -/* const */ uint64_t FPR_MASK64 = FPR_MASK | (uint64_t)FPR_MASK << 32; - -inline bool isFpr(Assembler::Register* reg) { - return reg->low >= N_GPRS; -} - -inline int fpr64(int reg) { return reg - N_GPRS; } -inline int fpr64(Assembler::Register* reg) { return fpr64(reg->low); } -inline int fpr32(int reg) { return fpr64(reg) << 1; } -inline int fpr32(Assembler::Register* reg) { return fpr64(reg) << 1; } - -const unsigned FrameHeaderSize = 1; - -const unsigned StackAlignmentInBytes = 8; -const unsigned StackAlignmentInWords -= StackAlignmentInBytes / TargetBytesPerWord; - -const int ThreadRegister = 8; -const int StackRegister = 13; -const int LinkRegister = 14; -const int ProgramCounter = 15; - -const int32_t PoolOffsetMask = 0xFFF; - -const bool DebugPool = false; - -class Context; -class MyBlock; -class PoolOffset; -class PoolEvent; - -void -resolve(MyBlock*); - -unsigned -padding(MyBlock*, unsigned); - -class MyBlock: public Assembler::Block { - public: - MyBlock(Context* context, unsigned offset): - context(context), next(0), poolOffsetHead(0), poolOffsetTail(0), - lastPoolOffsetTail(0), poolEventHead(0), poolEventTail(0), - lastEventOffset(0), offset(offset), start(~0), size(0) - { } - - virtual unsigned resolve(unsigned start, Assembler::Block* next) { - this->start = start; - this->next = static_cast(next); - - ::resolve(this); - - return start + size + padding(this, size); - } - - Context* context; - MyBlock* next; - PoolOffset* poolOffsetHead; - PoolOffset* poolOffsetTail; - PoolOffset* lastPoolOffsetTail; - PoolEvent* poolEventHead; - PoolEvent* poolEventTail; - unsigned lastEventOffset; - unsigned offset; - unsigned start; - unsigned size; -}; - -class Task; -class ConstantPoolEntry; - -class Context { - public: - Context(System* s, Allocator* a, Zone* zone): - s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), - firstBlock(new(zone) MyBlock(this, 0)), - lastBlock(firstBlock), poolOffsetHead(0), poolOffsetTail(0), - constantPool(0), constantPoolCount(0) - { } - - System* s; - Zone* zone; - Assembler::Client* client; - Vector code; - Task* tasks; - uint8_t* result; - MyBlock* firstBlock; - MyBlock* lastBlock; - PoolOffset* poolOffsetHead; - PoolOffset* poolOffsetTail; - ConstantPoolEntry* constantPool; - unsigned constantPoolCount; -}; - -class Task { - public: - Task(Task* next): next(next) { } - - virtual void run(Context* con) = 0; - - Task* next; -}; - -typedef void (*OperationType)(Context*); - -typedef void (*UnaryOperationType)(Context*, unsigned, Assembler::Operand*); - -typedef void (*BinaryOperationType) -(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*); - -typedef void (*TernaryOperationType) -(Context*, unsigned, Assembler::Operand*, Assembler::Operand*, - Assembler::Operand*); - -typedef void (*BranchOperationType) -(Context*, TernaryOperation, unsigned, Assembler::Operand*, - Assembler::Operand*, Assembler::Operand*); - -class ArchitectureContext { - public: - ArchitectureContext(System* s): s(s) { } - - System* s; - OperationType operations[OperationCount]; - UnaryOperationType unaryOperations[UnaryOperationCount - * OperandTypeCount]; - BinaryOperationType binaryOperations - [BinaryOperationCount * OperandTypeCount * OperandTypeCount]; - TernaryOperationType ternaryOperations - [NonBranchTernaryOperationCount * OperandTypeCount]; - BranchOperationType branchOperations - [BranchOperationCount * OperandTypeCount * OperandTypeCount]; -}; - -inline void NO_RETURN -abort(Context* con) -{ - abort(con->s); -} - -inline void NO_RETURN -abort(ArchitectureContext* con) -{ - abort(con->s); -} - -#ifndef NDEBUG -inline void -assert(Context* con, bool v) -{ - assert(con->s, v); -} - -inline void -assert(ArchitectureContext* con, bool v) -{ - assert(con->s, v); -} -#endif // not NDEBUG - -inline void -expect(Context* con, bool v) -{ - expect(con->s, v); -} - -class Offset: public Promise { - public: - Offset(Context* con, MyBlock* block, unsigned offset, bool forTrace): - con(con), block(block), offset(offset), forTrace(forTrace) - { } - - virtual bool resolved() { - return block->start != static_cast(~0); - } - - virtual int64_t value() { - assert(con, resolved()); - - unsigned o = offset - block->offset; - return block->start + padding - (block, forTrace ? o - TargetBytesPerWord : o) + o; - } - - Context* con; - MyBlock* block; - unsigned offset; - bool forTrace; -}; - -Promise* -offset(Context* con, bool forTrace = false) -{ - return new(con->zone) Offset(con, con->lastBlock, con->code.length(), forTrace); -} - -bool -bounded(int right, int left, int32_t v) -{ - return ((v << left) >> left) == v and ((v >> right) << right) == v; -} - -void* -updateOffset(System* s, uint8_t* instruction, int64_t value) -{ - // ARM's PC is two words ahead, and branches drop the bottom 2 bits. - int32_t v = (reinterpret_cast(value) - (instruction + 8)) >> 2; - - int32_t mask; - expect(s, bounded(0, 8, v)); - mask = 0xFFFFFF; - - int32_t* p = reinterpret_cast(instruction); - *p = (v & mask) | ((~mask) & *p); - - return instruction + 4; -} - -class OffsetListener: public Promise::Listener { - public: - OffsetListener(System* s, uint8_t* instruction): - s(s), - instruction(instruction) - { } - - virtual bool resolve(int64_t value, void** location) { - void* p = updateOffset(s, instruction, value); - if (location) *location = p; - return false; - } - - System* s; - uint8_t* instruction; -}; - -class OffsetTask: public Task { - public: - OffsetTask(Task* next, Promise* promise, Promise* instructionOffset): - Task(next), - promise(promise), - instructionOffset(instructionOffset) - { } - - virtual void run(Context* con) { - if (promise->resolved()) { - updateOffset - (con->s, con->result + instructionOffset->value(), promise->value()); - } else { - new (promise->listen(sizeof(OffsetListener))) - OffsetListener(con->s, con->result + instructionOffset->value()); - } - } - - Promise* promise; - Promise* instructionOffset; -}; - -void -appendOffsetTask(Context* con, Promise* promise, Promise* instructionOffset) -{ - con->tasks = new(con->zone) OffsetTask(con->tasks, promise, instructionOffset); -} - -inline unsigned -index(ArchitectureContext*, UnaryOperation operation, OperandType operand) -{ - return operation + (UnaryOperationCount * operand); -} - -inline unsigned -index(ArchitectureContext*, - BinaryOperation operation, - OperandType operand1, - OperandType operand2) -{ - return operation - + (BinaryOperationCount * operand1) - + (BinaryOperationCount * OperandTypeCount * operand2); -} - -bool -isBranch(TernaryOperation op) -{ - return op > FloatMin; -} - -bool -isFloatBranch(TernaryOperation op) -{ - return op > JumpIfNotEqual; -} - -inline unsigned -index(ArchitectureContext* con UNUSED, - TernaryOperation operation, - OperandType operand1) -{ - assert(con, not isBranch(operation)); - - return operation + (NonBranchTernaryOperationCount * operand1); -} - -unsigned -branchIndex(ArchitectureContext* con UNUSED, OperandType operand1, - OperandType operand2) -{ - return operand1 + (OperandTypeCount * operand2); -} - -// BEGIN OPERATION COMPILERS - -using namespace isa; - -// shortcut functions -inline void emit(Context* con, int code) { con->code.append4(code); } - -inline int newTemp(Context* con) { - return con->client->acquireTemporary(GPR_MASK); -} - -inline int newTemp(Context* con, unsigned mask) { - return con->client->acquireTemporary(mask); -} - -inline void freeTemp(Context* con, int r) { - con->client->releaseTemporary(r); -} - -inline int64_t getValue(Assembler::Constant* con) { - return con->value->value(); -} - -inline Assembler::Register makeTemp(Context* con) { - Assembler::Register tmp(newTemp(con)); - return tmp; -} - -inline Assembler::Register makeTemp64(Context* con) { - Assembler::Register tmp(newTemp(con), newTemp(con)); - return tmp; -} - -inline void freeTemp(Context* con, const Assembler::Register& tmp) { - if (tmp.low != NoRegister) freeTemp(con, tmp.low); - if (tmp.high != NoRegister) freeTemp(con, tmp.high); -} - -inline void -write4(uint8_t* dst, uint32_t v) -{ - memcpy(dst, &v, 4); -} - -void -andC(Context* con, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst); - -void shiftLeftR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - if (size == 8) { - int tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); - ResolvedPromise maskPromise(0x3F); - Assembler::Constant mask(&maskPromise); - Assembler::Register dst(tmp3); - andC(con, 4, &mask, a, &dst); - emit(con, lsl(tmp1, b->high, tmp3)); - emit(con, rsbi(tmp2, tmp3, 32)); - emit(con, orrsh(tmp1, tmp1, b->low, tmp2, LSR)); - emit(con, SETS(subi(t->high, tmp3, 32))); - emit(con, SETCOND(mov(t->high, tmp1), MI)); - emit(con, SETCOND(lsl(t->high, b->low, t->high), PL)); - emit(con, lsl(t->low, b->low, tmp3)); - freeTemp(con, tmp1); freeTemp(con, tmp2); freeTemp(con, tmp3); - } else { - int tmp = newTemp(con); - ResolvedPromise maskPromise(0x1F); - Assembler::Constant mask(&maskPromise); - Assembler::Register dst(tmp); - andC(con, size, &mask, a, &dst); - emit(con, lsl(t->low, b->low, tmp)); - freeTemp(con, tmp); - } -} - -void -moveRR(Context* con, unsigned srcSize, Assembler::Register* src, - unsigned dstSize, Assembler::Register* dst); - -void shiftLeftC(Context* con, unsigned size UNUSED, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - assert(con, size == TargetBytesPerWord); - if (getValue(a) & 0x1F) { - emit(con, lsli(t->low, b->low, getValue(a) & 0x1F)); - } else { - moveRR(con, size, b, size, t); - } -} - -void shiftRightR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - if (size == 8) { - int tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); - ResolvedPromise maskPromise(0x3F); - Assembler::Constant mask(&maskPromise); - Assembler::Register dst(tmp3); - andC(con, 4, &mask, a, &dst); - emit(con, lsr(tmp1, b->low, tmp3)); - emit(con, rsbi(tmp2, tmp3, 32)); - emit(con, orrsh(tmp1, tmp1, b->high, tmp2, LSL)); - emit(con, SETS(subi(t->low, tmp3, 32))); - emit(con, SETCOND(mov(t->low, tmp1), MI)); - emit(con, SETCOND(asr(t->low, b->high, t->low), PL)); - emit(con, asr(t->high, b->high, tmp3)); - freeTemp(con, tmp1); freeTemp(con, tmp2); freeTemp(con, tmp3); - } else { - int tmp = newTemp(con); - ResolvedPromise maskPromise(0x1F); - Assembler::Constant mask(&maskPromise); - Assembler::Register dst(tmp); - andC(con, size, &mask, a, &dst); - emit(con, asr(t->low, b->low, tmp)); - freeTemp(con, tmp); - } -} - -void shiftRightC(Context* con, unsigned size UNUSED, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - assert(con, size == TargetBytesPerWord); - if (getValue(a) & 0x1F) { - emit(con, asri(t->low, b->low, getValue(a) & 0x1F)); - } else { - moveRR(con, size, b, size, t); - } -} - -void unsignedShiftRightR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - int tmpShift = newTemp(con); - ResolvedPromise maskPromise(size == 8 ? 0x3F : 0x1F); - Assembler::Constant mask(&maskPromise); - Assembler::Register dst(tmpShift); - andC(con, 4, &mask, a, &dst); - emit(con, lsr(t->low, b->low, tmpShift)); - if (size == 8) { - int tmpHi = newTemp(con), tmpLo = newTemp(con); - emit(con, SETS(rsbi(tmpHi, tmpShift, 32))); - emit(con, lsl(tmpLo, b->high, tmpHi)); - emit(con, orr(t->low, t->low, tmpLo)); - emit(con, addi(tmpHi, tmpShift, -32)); - emit(con, lsr(tmpLo, b->high, tmpHi)); - emit(con, orr(t->low, t->low, tmpLo)); - emit(con, lsr(t->high, b->high, tmpShift)); - freeTemp(con, tmpHi); freeTemp(con, tmpLo); - } - freeTemp(con, tmpShift); -} - -void unsignedShiftRightC(Context* con, unsigned size UNUSED, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - assert(con, size == TargetBytesPerWord); - if (getValue(a) & 0x1F) { - emit(con, lsri(t->low, b->low, getValue(a) & 0x1F)); - } else { - moveRR(con, size, b, size, t); - } -} - -class ConstantPoolEntry: public Promise { - public: - ConstantPoolEntry(Context* con, Promise* constant, ConstantPoolEntry* next, - Promise* callOffset): - con(con), constant(constant), next(next), callOffset(callOffset), - address(0) - { } - - virtual int64_t value() { - assert(con, resolved()); - - return reinterpret_cast(address); - } - - virtual bool resolved() { - return address != 0; - } - - Context* con; - Promise* constant; - ConstantPoolEntry* next; - Promise* callOffset; - void* address; - unsigned constantPoolCount; -}; - -class ConstantPoolListener: public Promise::Listener { - public: - ConstantPoolListener(System* s, target_uintptr_t* address, - uint8_t* returnAddress): - s(s), - address(address), - returnAddress(returnAddress) - { } - - virtual bool resolve(int64_t value, void** location) { - *address = value; - if (location) { - *location = returnAddress ? static_cast(returnAddress) : address; - } - return true; - } - - System* s; - target_uintptr_t* address; - uint8_t* returnAddress; -}; - -class PoolOffset { - public: - PoolOffset(MyBlock* block, ConstantPoolEntry* entry, unsigned offset): - block(block), entry(entry), next(0), offset(offset) - { } - - MyBlock* block; - ConstantPoolEntry* entry; - PoolOffset* next; - unsigned offset; -}; - -class PoolEvent { - public: - PoolEvent(PoolOffset* poolOffsetHead, PoolOffset* poolOffsetTail, - unsigned offset): - poolOffsetHead(poolOffsetHead), poolOffsetTail(poolOffsetTail), next(0), - offset(offset) - { } - - PoolOffset* poolOffsetHead; - PoolOffset* poolOffsetTail; - PoolEvent* next; - unsigned offset; -}; - -void -appendConstantPoolEntry(Context* con, Promise* constant, Promise* callOffset) -{ - if (constant->resolved()) { - // make a copy, since the original might be allocated on the - // stack, and we need our copy to live until assembly is complete - constant = new(con->zone) ResolvedPromise(constant->value()); - } - - con->constantPool = new(con->zone) ConstantPoolEntry(con, constant, con->constantPool, callOffset); - - ++ con->constantPoolCount; - - PoolOffset* o = new(con->zone) PoolOffset(con->lastBlock, con->constantPool, con->code.length() - con->lastBlock->offset); - - if (DebugPool) { - fprintf(stderr, "add pool offset %p %d to block %p\n", - o, o->offset, con->lastBlock); - } - - if (con->lastBlock->poolOffsetTail) { - con->lastBlock->poolOffsetTail->next = o; - } else { - con->lastBlock->poolOffsetHead = o; - } - con->lastBlock->poolOffsetTail = o; -} - -void -appendPoolEvent(Context* con, MyBlock* b, unsigned offset, PoolOffset* head, - PoolOffset* tail) -{ - PoolEvent* e = new(con->zone) PoolEvent(head, tail, offset); - - if (b->poolEventTail) { - b->poolEventTail->next = e; - } else { - b->poolEventHead = e; - } - b->poolEventTail = e; -} - -bool -needJump(MyBlock* b) -{ - return b->next or b->size != (b->size & PoolOffsetMask); -} - -unsigned -padding(MyBlock* b, unsigned offset) -{ - unsigned total = 0; - for (PoolEvent* e = b->poolEventHead; e; e = e->next) { - if (e->offset <= offset) { - if (needJump(b)) { - total += TargetBytesPerWord; - } - for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { - total += TargetBytesPerWord; - } - } else { - break; - } - } - return total; -} - -void -resolve(MyBlock* b) -{ - Context* con = b->context; - - if (b->poolOffsetHead) { - if (con->poolOffsetTail) { - con->poolOffsetTail->next = b->poolOffsetHead; - } else { - con->poolOffsetHead = b->poolOffsetHead; - } - con->poolOffsetTail = b->poolOffsetTail; - } - - if (con->poolOffsetHead) { - bool append; - if (b->next == 0 or b->next->poolEventHead) { - append = true; - } else { - int32_t v = (b->start + b->size + b->next->size + TargetBytesPerWord - 8) - - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); - - append = (v != (v & PoolOffsetMask)); - - if (DebugPool) { - fprintf(stderr, - "current %p %d %d next %p %d %d\n", - b, b->start, b->size, b->next, b->start + b->size, - b->next->size); - fprintf(stderr, - "offset %p %d is of distance %d to next block; append? %d\n", - con->poolOffsetHead, con->poolOffsetHead->offset, v, append); - } - } - - if (append) { -#ifndef NDEBUG - int32_t v = (b->start + b->size - 8) - - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); - - expect(con, v == (v & PoolOffsetMask)); -#endif // not NDEBUG - - appendPoolEvent(con, b, b->size, con->poolOffsetHead, con->poolOffsetTail); - - if (DebugPool) { - for (PoolOffset* o = con->poolOffsetHead; o; o = o->next) { - fprintf(stderr, - "include %p %d in pool event %p at offset %d in block %p\n", - o, o->offset, b->poolEventTail, b->size, b); - } - } - - con->poolOffsetHead = 0; - con->poolOffsetTail = 0; - } - } -} - -void -jumpR(Context* con, unsigned size UNUSED, Assembler::Register* target) -{ - assert(con, size == TargetBytesPerWord); - emit(con, bx(target->low)); -} - -void -swapRR(Context* con, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - assert(con, aSize == TargetBytesPerWord); - assert(con, bSize == TargetBytesPerWord); - - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveRR(con, aSize, a, bSize, &tmp); - moveRR(con, bSize, b, aSize, a); - moveRR(con, bSize, &tmp, bSize, b); - con->client->releaseTemporary(tmp.low); -} - -void -moveRR(Context* con, unsigned srcSize, Assembler::Register* src, - unsigned dstSize, Assembler::Register* dst) -{ - bool srcIsFpr = isFpr(src); - bool dstIsFpr = isFpr(dst); - if (srcIsFpr || dstIsFpr) { // FPR(s) involved - assert(con, srcSize == dstSize); - const bool dprec = srcSize == 8; - if (srcIsFpr && dstIsFpr) { // FPR to FPR - if (dprec) emit(con, fcpyd(fpr64(dst), fpr64(src))); // double - else emit(con, fcpys(fpr32(dst), fpr32(src))); // single - } else if (srcIsFpr) { // FPR to GPR - if (dprec) emit(con, fmrrd(dst->low, dst->high, fpr64(src))); - else emit(con, fmrs(dst->low, fpr32(src))); - } else { // GPR to FPR - if (dprec) emit(con, fmdrr(fpr64(dst->low), src->low, src->high)); - else emit(con, fmsr(fpr32(dst), src->low)); - } - return; - } - - switch (srcSize) { - case 1: - emit(con, lsli(dst->low, src->low, 24)); - emit(con, asri(dst->low, dst->low, 24)); - break; - - case 2: - emit(con, lsli(dst->low, src->low, 16)); - emit(con, asri(dst->low, dst->low, 16)); - break; - - case 4: - case 8: - if (srcSize == 4 and dstSize == 8) { - moveRR(con, 4, src, 4, dst); - emit(con, asri(dst->high, src->low, 31)); - } else if (srcSize == 8 and dstSize == 8) { - Assembler::Register srcHigh(src->high); - Assembler::Register dstHigh(dst->high); - - if (src->high == dst->low) { - if (src->low == dst->high) { - swapRR(con, 4, src, 4, dst); - } else { - moveRR(con, 4, &srcHigh, 4, &dstHigh); - moveRR(con, 4, src, 4, dst); - } - } else { - moveRR(con, 4, src, 4, dst); - moveRR(con, 4, &srcHigh, 4, &dstHigh); - } - } else if (src->low != dst->low) { - emit(con, mov(dst->low, src->low)); - } - break; - - default: abort(con); - } -} - -void -moveZRR(Context* con, unsigned srcSize, Assembler::Register* src, - unsigned, Assembler::Register* dst) -{ - switch (srcSize) { - case 2: - emit(con, lsli(dst->low, src->low, 16)); - emit(con, lsri(dst->low, dst->low, 16)); - break; - - default: abort(con); - } -} - -void moveCR(Context* con, unsigned size, Assembler::Constant* src, - unsigned, Assembler::Register* dst); - -void -moveCR2(Context* con, unsigned size, Assembler::Constant* src, - Assembler::Register* dst, Promise* callOffset) -{ - if (isFpr(dst)) { // floating-point - Assembler::Register tmp = size > 4 ? makeTemp64(con) : - makeTemp(con); - moveCR(con, size, src, size, &tmp); - moveRR(con, size, &tmp, size, dst); - freeTemp(con, tmp); - } else if (size > 4) { - uint64_t value = (uint64_t)src->value->value(); - ResolvedPromise loBits(value & MASK_LO32); - Assembler::Constant srcLo(&loBits); - ResolvedPromise hiBits(value >> 32); - Assembler::Constant srcHi(&hiBits); - Assembler::Register dstHi(dst->high); - moveCR(con, 4, &srcLo, 4, dst); - moveCR(con, 4, &srcHi, 4, &dstHi); - } else if (src->value->resolved() and isOfWidth(getValue(src), 8)) { - emit(con, movi(dst->low, lo8(getValue(src)))); // fits in immediate - } else { - appendConstantPoolEntry(con, src->value, callOffset); - emit(con, ldri(dst->low, ProgramCounter, 0)); // load 32 bits - } -} - -void -moveCR(Context* con, unsigned size, Assembler::Constant* src, - unsigned, Assembler::Register* dst) -{ - moveCR2(con, size, src, dst, 0); -} - -void addR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, SETS(add(t->low, a->low, b->low))); - emit(con, adc(t->high, a->high, b->high)); - } else { - emit(con, add(t->low, a->low, b->low)); - } -} - -void subR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, SETS(rsb(t->low, a->low, b->low))); - emit(con, rsc(t->high, a->high, b->high)); - } else { - emit(con, rsb(t->low, a->low, b->low)); - } -} - -void -addC(Context* con, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - assert(con, size == TargetBytesPerWord); - - int32_t v = a->value->value(); - if (v) { - if (v > 0 and v < 256) { - emit(con, addi(dst->low, b->low, v)); - } else if (v > 0 and v < 1024 and v % 4 == 0) { - emit(con, addi(dst->low, b->low, v >> 2, 15)); - } else { - // todo - abort(con); - } - } else { - moveRR(con, size, b, size, dst); - } -} - -void -subC(Context* con, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - assert(con, size == TargetBytesPerWord); - - int32_t v = a->value->value(); - if (v) { - if (v > 0 and v < 256) { - emit(con, subi(dst->low, b->low, v)); - } else if (v > 0 and v < 1024 and v % 4 == 0) { - emit(con, subi(dst->low, b->low, v >> 2, 15)); - } else { - // todo - abort(con); - } - } else { - moveRR(con, size, b, size, dst); - } -} - -void multiplyR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - bool useTemporaries = b->low == t->low; - int tmpLow = useTemporaries ? con->client->acquireTemporary(GPR_MASK) : t->low; - int tmpHigh = useTemporaries ? con->client->acquireTemporary(GPR_MASK) : t->high; - - emit(con, umull(tmpLow, tmpHigh, a->low, b->low)); - emit(con, mla(tmpHigh, a->low, b->high, tmpHigh)); - emit(con, mla(tmpHigh, a->high, b->low, tmpHigh)); - - if (useTemporaries) { - emit(con, mov(t->low, tmpLow)); - emit(con, mov(t->high, tmpHigh)); - con->client->releaseTemporary(tmpLow); - con->client->releaseTemporary(tmpHigh); - } - } else { - emit(con, mul(t->low, a->low, b->low)); - } -} - -void floatAbsoluteRR(Context* con, unsigned size, Assembler::Register* a, unsigned, Assembler::Register* b) { - if (size == 8) { - emit(con, fabsd(fpr64(b), fpr64(a))); - } else { - emit(con, fabss(fpr32(b), fpr32(a))); - } -} - -void floatNegateRR(Context* con, unsigned size, Assembler::Register* a, unsigned, Assembler::Register* b) { - if (size == 8) { - emit(con, fnegd(fpr64(b), fpr64(a))); - } else { - emit(con, fnegs(fpr32(b), fpr32(a))); - } -} - -void float2FloatRR(Context* con, unsigned size, Assembler::Register* a, unsigned, Assembler::Register* b) { - if (size == 8) { - emit(con, fcvtsd(fpr32(b), fpr64(a))); - } else { - emit(con, fcvtds(fpr64(b), fpr32(a))); - } -} - -void float2IntRR(Context* con, unsigned size, Assembler::Register* a, unsigned, Assembler::Register* b) { - int tmp = newTemp(con, FPR_MASK); - int ftmp = fpr32(tmp); - if (size == 8) { // double to int - emit(con, ftosizd(ftmp, fpr64(a))); - } else { // float to int - emit(con, ftosizs(ftmp, fpr32(a))); - } // else thunked - emit(con, fmrs(b->low, ftmp)); - freeTemp(con, tmp); -} - -void int2FloatRR(Context* con, unsigned, Assembler::Register* a, unsigned size, Assembler::Register* b) { - emit(con, fmsr(fpr32(b), a->low)); - if (size == 8) { // int to double - emit(con, fsitod(fpr64(b), fpr32(b))); - } else { // int to float - emit(con, fsitos(fpr32(b), fpr32(b))); - } // else thunked -} - -void floatSqrtRR(Context* con, unsigned size, Assembler::Register* a, unsigned, Assembler::Register* b) { - if (size == 8) { - emit(con, fsqrtd(fpr64(b), fpr64(a))); - } else { - emit(con, fsqrts(fpr32(b), fpr32(a))); - } -} - -void floatAddR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, faddd(fpr64(t), fpr64(a), fpr64(b))); - } else { - emit(con, fadds(fpr32(t), fpr32(a), fpr32(b))); - } -} - -void floatSubtractR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, fsubd(fpr64(t), fpr64(b), fpr64(a))); - } else { - emit(con, fsubs(fpr32(t), fpr32(b), fpr32(a))); - } -} - -void floatMultiplyR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, fmuld(fpr64(t), fpr64(a), fpr64(b))); - } else { - emit(con, fmuls(fpr32(t), fpr32(a), fpr32(b))); - } -} - -void floatDivideR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if (size == 8) { - emit(con, fdivd(fpr64(t), fpr64(b), fpr64(a))); - } else { - emit(con, fdivs(fpr32(t), fpr32(b), fpr32(a))); - } -} - -int -normalize(Context* con, int offset, int index, unsigned scale, - bool* preserveIndex, bool* release) -{ - if (offset != 0 or scale != 1) { - Assembler::Register normalizedIndex - (*preserveIndex ? con->client->acquireTemporary(GPR_MASK) : index); - - if (*preserveIndex) { - *release = true; - *preserveIndex = false; - } else { - *release = false; - } - - int scaled; - - if (scale != 1) { - Assembler::Register unscaledIndex(index); - - ResolvedPromise scalePromise(log(scale)); - Assembler::Constant scaleConstant(&scalePromise); - - shiftLeftC(con, TargetBytesPerWord, &scaleConstant, - &unscaledIndex, &normalizedIndex); - - scaled = normalizedIndex.low; - } else { - scaled = index; - } - - if (offset != 0) { - Assembler::Register untranslatedIndex(scaled); - - ResolvedPromise offsetPromise(offset); - Assembler::Constant offsetConstant(&offsetPromise); - - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveCR(con, TargetBytesPerWord, &offsetConstant, TargetBytesPerWord, &tmp); - addR(con, TargetBytesPerWord, &tmp, &untranslatedIndex, &normalizedIndex); - con->client->releaseTemporary(tmp.low); - } - - return normalizedIndex.low; - } else { - *release = false; - return index; - } -} - -void -store(Context* con, unsigned size, Assembler::Register* src, - int base, int offset, int index, unsigned scale, bool preserveIndex) -{ - if (index != NoRegister) { - bool release; - int normalized = normalize - (con, offset, index, scale, &preserveIndex, &release); - - if (!isFpr(src)) { // GPR store - switch (size) { - case 1: - emit(con, strb(src->low, base, normalized)); - break; - - case 2: - emit(con, strh(src->low, base, normalized)); - break; - - case 4: - emit(con, str(src->low, base, normalized)); - break; - - case 8: { // split into 2 32-bit stores - Assembler::Register srcHigh(src->high); - store(con, 4, &srcHigh, base, 0, normalized, 1, preserveIndex); - store(con, 4, src, base, 4, normalized, 1, preserveIndex); - } break; - - default: abort(con); - } - } else { // FPR store - Assembler::Register base_(base), - normalized_(normalized), - absAddr = makeTemp(con); - // FPR stores have only bases, so we must add the index - addR(con, TargetBytesPerWord, &base_, &normalized_, &absAddr); - // double-precision - if (size == 8) emit(con, fstd(fpr64(src), absAddr.low)); - // single-precision - else emit(con, fsts(fpr32(src), absAddr.low)); - freeTemp(con, absAddr); - } - - if (release) con->client->releaseTemporary(normalized); - } else if (size == 8 - or abs(offset) == (abs(offset) & 0xFF) - or (size != 2 and abs(offset) == (abs(offset) & 0xFFF))) - { - if (!isFpr(src)) { // GPR store - switch (size) { - case 1: - emit(con, strbi(src->low, base, offset)); - break; - - case 2: - emit(con, strhi(src->low, base, offset)); - break; - - case 4: - emit(con, stri(src->low, base, offset)); - break; - - case 8: { // split into 2 32-bit stores - Assembler::Register srcHigh(src->high); - store(con, 4, &srcHigh, base, offset, NoRegister, 1, false); - store(con, 4, src, base, offset + 4, NoRegister, 1, false); - } break; - - default: abort(con); - } - } else { // FPR store - // double-precision - if (size == 8) emit(con, fstd(fpr64(src), base, offset)); - // single-precision - else emit(con, fsts(fpr32(src), base, offset)); - } - } else { - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - ResolvedPromise offsetPromise(offset); - Assembler::Constant offsetConstant(&offsetPromise); - moveCR(con, TargetBytesPerWord, &offsetConstant, - TargetBytesPerWord, &tmp); - - store(con, size, src, base, 0, tmp.low, 1, false); - - con->client->releaseTemporary(tmp.low); - } -} - -void -moveRM(Context* con, unsigned srcSize, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Memory* dst) -{ - assert(con, srcSize == dstSize); - - store(con, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); -} - -void -moveAndUpdateRM(Context* con, unsigned srcSize UNUSED, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Memory* dst) -{ - assert(con, srcSize == TargetBytesPerWord); - assert(con, dstSize == TargetBytesPerWord); - - if (dst->index == NoRegister) { - emit(con, stri(src->low, dst->base, dst->offset, dst->offset ? 1 : 0)); - } else { - assert(con, dst->offset == 0); - assert(con, dst->scale == 1); - - emit(con, str(src->low, dst->base, dst->index, 1)); - } -} - -void -load(Context* con, unsigned srcSize, int base, int offset, int index, - unsigned scale, unsigned dstSize, Assembler::Register* dst, - bool preserveIndex, bool signExtend) -{ - if (index != NoRegister) { - bool release; - int normalized = normalize - (con, offset, index, scale, &preserveIndex, &release); - - if (!isFpr(dst)) { // GPR load - switch (srcSize) { - case 1: - if (signExtend) { - emit(con, ldrsb(dst->low, base, normalized)); - } else { - emit(con, ldrb(dst->low, base, normalized)); - } - break; - - case 2: - if (signExtend) { - emit(con, ldrsh(dst->low, base, normalized)); - } else { - emit(con, ldrh(dst->low, base, normalized)); - } - break; - - case 4: - case 8: { - if (srcSize == 4 and dstSize == 8) { - load(con, 4, base, 0, normalized, 1, 4, dst, preserveIndex, - false); - moveRR(con, 4, dst, 8, dst); - } else if (srcSize == 8 and dstSize == 8) { - Assembler::Register dstHigh(dst->high); - load(con, 4, base, 0, normalized, 1, 4, &dstHigh, - preserveIndex, false); - load(con, 4, base, 4, normalized, 1, 4, dst, preserveIndex, - false); - } else { - emit(con, ldr(dst->low, base, normalized)); - } - } break; - - default: abort(con); - } - } else { // FPR load - Assembler::Register base_(base), - normalized_(normalized), - absAddr = makeTemp(con); - // VFP loads only have bases, so we must add the index - addR(con, TargetBytesPerWord, &base_, &normalized_, &absAddr); - // double-precision - if (srcSize == 8) emit(con, fldd(fpr64(dst), absAddr.low)); - // single-precision - else emit(con, flds(fpr32(dst), absAddr.low)); - freeTemp(con, absAddr); - } - - if (release) con->client->releaseTemporary(normalized); - } else if ((srcSize == 8 and dstSize == 8) - or abs(offset) == (abs(offset) & 0xFF) - or (srcSize != 2 - and (srcSize != 1 or not signExtend) - and abs(offset) == (abs(offset) & 0xFFF))) - { - if (!isFpr(dst)) { // GPR load - switch (srcSize) { - case 1: - if (signExtend) { - emit(con, ldrsbi(dst->low, base, offset)); - } else { - emit(con, ldrbi(dst->low, base, offset)); - } - break; - - case 2: - if (signExtend) { - emit(con, ldrshi(dst->low, base, offset)); - } else { - emit(con, ldrhi(dst->low, base, offset)); - } - break; - - case 4: - emit(con, ldri(dst->low, base, offset)); - break; - - case 8: { - if (dstSize == 8) { - Assembler::Register dstHigh(dst->high); - load(con, 4, base, offset, NoRegister, 1, 4, &dstHigh, false, - false); - load(con, 4, base, offset + 4, NoRegister, 1, 4, dst, false, - false); - } else { - emit(con, ldri(dst->low, base, offset)); - } - } break; - - default: abort(con); - } - } else { // FPR load - // double-precision - if (srcSize == 8) emit(con, fldd(fpr64(dst), base, offset)); - // single-precision - else emit(con, flds(fpr32(dst), base, offset)); - } - } else { - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - ResolvedPromise offsetPromise(offset); - Assembler::Constant offsetConstant(&offsetPromise); - moveCR(con, TargetBytesPerWord, &offsetConstant, TargetBytesPerWord, - &tmp); - - load(con, srcSize, base, 0, tmp.low, 1, dstSize, dst, false, - signExtend); - - con->client->releaseTemporary(tmp.low); - } -} - -void -moveMR(Context* con, unsigned srcSize, Assembler::Memory* src, - unsigned dstSize, Assembler::Register* dst) -{ - load(con, srcSize, src->base, src->offset, src->index, src->scale, - dstSize, dst, true, true); -} - -void -moveZMR(Context* con, unsigned srcSize, Assembler::Memory* src, - unsigned dstSize, Assembler::Register* dst) -{ - load(con, srcSize, src->base, src->offset, src->index, src->scale, - dstSize, dst, true, false); -} - -void -andR(Context* con, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) emit(con, and_(dst->high, a->high, b->high)); - emit(con, and_(dst->low, a->low, b->low)); -} - -void -andC(Context* con, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - int64_t v = a->value->value(); - - if (size == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - andC(con, 4, &al, b, dst); - andC(con, 4, &ah, &bh, &dh); - } else { - uint32_t v32 = static_cast(v); - if (v32 != 0xFFFFFFFF) { - if ((v32 & 0xFFFFFF00) == 0xFFFFFF00) { - emit(con, bici(dst->low, b->low, (~(v32 & 0xFF)) & 0xFF)); - } else if ((v32 & 0xFFFFFF00) == 0) { - emit(con, andi(dst->low, b->low, v32 & 0xFF)); - } else { - // todo: there are other cases we can handle in one - // instruction - - bool useTemporary = b->low == dst->low; - Assembler::Register tmp(dst->low); - if (useTemporary) { - tmp.low = con->client->acquireTemporary(GPR_MASK); - } - - moveCR(con, 4, a, 4, &tmp); - andR(con, 4, b, &tmp, dst); - - if (useTemporary) { - con->client->releaseTemporary(tmp.low); - } - } - } else { - moveRR(con, size, b, size, dst); - } - } -} - -void -orR(Context* con, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) emit(con, orr(dst->high, a->high, b->high)); - emit(con, orr(dst->low, a->low, b->low)); -} - -void -xorR(Context* con, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) emit(con, eor(dst->high, a->high, b->high)); - emit(con, eor(dst->low, a->low, b->low)); -} - -void -moveAR2(Context* con, unsigned srcSize, Assembler::Address* src, - unsigned dstSize, Assembler::Register* dst) -{ - assert(con, srcSize == 4 and dstSize == 4); - - Assembler::Constant constant(src->address); - moveCR(con, srcSize, &constant, dstSize, dst); - - Assembler::Memory memory(dst->low, 0, -1, 0); - moveMR(con, dstSize, &memory, dstSize, dst); -} - -void -moveAR(Context* con, unsigned srcSize, Assembler::Address* src, - unsigned dstSize, Assembler::Register* dst) -{ - moveAR2(con, srcSize, src, dstSize, dst); -} - -void -compareRR(Context* con, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(con, !(isFpr(a) ^ isFpr(b))); // regs must be of the same type - - if (!isFpr(a)) { // GPR compare - assert(con, aSize == 4 && bSize == 4); - /**///assert(con, b->low != a->low); - emit(con, cmp(b->low, a->low)); - } else { // FPR compare - assert(con, aSize == bSize); - if (aSize == 8) emit(con, fcmpd(fpr64(b), fpr64(a))); // double - else emit(con, fcmps(fpr32(b), fpr32(a))); // single - emit(con, fmstat()); - } -} - -void -compareCR(Context* con, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(con, aSize == 4 and bSize == 4); - - if (!isFpr(b) && a->value->resolved() && - isOfWidth(a->value->value(), 8)) { - emit(con, cmpi(b->low, a->value->value())); - } else { - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveCR(con, aSize, a, bSize, &tmp); - compareRR(con, bSize, &tmp, bSize, b); - con->client->releaseTemporary(tmp.low); - } -} - -void -compareCM(Context* con, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Memory* b) -{ - assert(con, aSize == 4 and bSize == 4); - - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveMR(con, bSize, b, bSize, &tmp); - compareCR(con, aSize, a, bSize, &tmp); - con->client->releaseTemporary(tmp.low); -} - -void -compareRM(Context* con, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Memory* b) -{ - assert(con, aSize == 4 and bSize == 4); - - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveMR(con, bSize, b, bSize, &tmp); - compareRR(con, aSize, a, bSize, &tmp); - con->client->releaseTemporary(tmp.low); -} - -int32_t -branch(Context* con, TernaryOperation op) -{ - switch (op) { - case JumpIfEqual: - case JumpIfFloatEqual: - return beq(0); - - case JumpIfNotEqual: - case JumpIfFloatNotEqual: - return bne(0); - - case JumpIfLess: - case JumpIfFloatLess: - case JumpIfFloatLessOrUnordered: - return blt(0); - - case JumpIfGreater: - case JumpIfFloatGreater: - return bgt(0); - - case JumpIfLessOrEqual: - case JumpIfFloatLessOrEqual: - case JumpIfFloatLessOrEqualOrUnordered: - return ble(0); - - case JumpIfGreaterOrEqual: - case JumpIfFloatGreaterOrEqual: - return bge(0); - - case JumpIfFloatGreaterOrUnordered: - return bhi(0); - - case JumpIfFloatGreaterOrEqualOrUnordered: - return bpl(0); - - default: - abort(con); - } -} - -void -conditional(Context* con, int32_t branch, Assembler::Constant* target) -{ - appendOffsetTask(con, target->value, offset(con)); - emit(con, branch); -} - -void -branch(Context* con, TernaryOperation op, Assembler::Constant* target) -{ - conditional(con, branch(con, op), target); -} - -void -branchLong(Context* con, TernaryOperation op, Assembler::Operand* al, - Assembler::Operand* ah, Assembler::Operand* bl, - Assembler::Operand* bh, Assembler::Constant* target, - BinaryOperationType compareSigned, - BinaryOperationType compareUnsigned) -{ - compareSigned(con, 4, ah, 4, bh); - - unsigned next = 0; - - switch (op) { - case JumpIfEqual: - case JumpIfFloatEqual: - next = con->code.length(); - emit(con, bne(0)); - - compareSigned(con, 4, al, 4, bl); - conditional(con, beq(0), target); - break; - - case JumpIfNotEqual: - case JumpIfFloatNotEqual: - conditional(con, bne(0), target); - - compareSigned(con, 4, al, 4, bl); - conditional(con, bne(0), target); - break; - - case JumpIfLess: - case JumpIfFloatLess: - conditional(con, blt(0), target); - - next = con->code.length(); - emit(con, bgt(0)); - - compareUnsigned(con, 4, al, 4, bl); - conditional(con, blo(0), target); - break; - - case JumpIfGreater: - case JumpIfFloatGreater: - conditional(con, bgt(0), target); - - next = con->code.length(); - emit(con, blt(0)); - - compareUnsigned(con, 4, al, 4, bl); - conditional(con, bhi(0), target); - break; - - case JumpIfLessOrEqual: - case JumpIfFloatLessOrEqual: - conditional(con, blt(0), target); - - next = con->code.length(); - emit(con, bgt(0)); - - compareUnsigned(con, 4, al, 4, bl); - conditional(con, bls(0), target); - break; - - case JumpIfGreaterOrEqual: - case JumpIfFloatGreaterOrEqual: - conditional(con, bgt(0), target); - - next = con->code.length(); - emit(con, blt(0)); - - compareUnsigned(con, 4, al, 4, bl); - conditional(con, bhs(0), target); - break; - - default: - abort(con); - } - - if (next) { - updateOffset - (con->s, con->code.data + next, reinterpret_cast - (con->code.data + con->code.length())); - } -} - -void -branchRR(Context* con, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Register* b, - Assembler::Constant* target) -{ - if (!isFpr(a) && size > TargetBytesPerWord) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - branchLong(con, op, a, &ah, b, &bh, target, CAST2(compareRR), - CAST2(compareRR)); - } else { - compareRR(con, size, a, size, b); - branch(con, op, target); - } -} - -void -branchCR(Context* con, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Register* b, - Assembler::Constant* target) -{ - assert(con, !isFloatBranch(op)); - - if (size > TargetBytesPerWord) { - int64_t v = a->value->value(); - - ResolvedPromise low(v & ~static_cast(0)); - Assembler::Constant al(&low); - - ResolvedPromise high((v >> 32) & ~static_cast(0)); - Assembler::Constant ah(&high); - - Assembler::Register bh(b->high); - - branchLong(con, op, &al, &ah, b, &bh, target, CAST2(compareCR), - CAST2(compareCR)); - } else { - compareCR(con, size, a, size, b); - branch(con, op, target); - } -} - -void -branchRM(Context* con, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(con, !isFloatBranch(op)); - assert(con, size <= TargetBytesPerWord); - - compareRM(con, size, a, size, b); - branch(con, op, target); -} - -void -branchCM(Context* con, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(con, !isFloatBranch(op)); - assert(con, size <= TargetBytesPerWord); - - compareCM(con, size, a, size, b); - branch(con, op, target); -} - -ShiftMaskPromise* -shiftMaskPromise(Context* con, Promise* base, unsigned shift, int64_t mask) -{ - return new(con->zone) ShiftMaskPromise(base, shift, mask); -} - -void -moveCM(Context* con, unsigned srcSize, Assembler::Constant* src, - unsigned dstSize, Assembler::Memory* dst) -{ - switch (dstSize) { - case 8: { - Assembler::Constant srcHigh - (shiftMaskPromise(con, src->value, 32, 0xFFFFFFFF)); - Assembler::Constant srcLow - (shiftMaskPromise(con, src->value, 0, 0xFFFFFFFF)); - - Assembler::Memory dstLow - (dst->base, dst->offset + 4, dst->index, dst->scale); - - moveCM(con, 4, &srcLow, 4, &dstLow); - moveCM(con, 4, &srcHigh, 4, dst); - } break; - - default: - Assembler::Register tmp(con->client->acquireTemporary(GPR_MASK)); - moveCR(con, srcSize, src, dstSize, &tmp); - moveRM(con, dstSize, &tmp, dstSize, dst); - con->client->releaseTemporary(tmp.low); - } -} - -void -negateRR(Context* con, unsigned srcSize, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Register* dst) -{ - assert(con, srcSize == dstSize); - - emit(con, mvn(dst->low, src->low)); - emit(con, SETS(addi(dst->low, dst->low, 1))); - if (srcSize == 8) { - emit(con, mvn(dst->high, src->high)); - emit(con, adci(dst->high, dst->high, 0)); - } -} - -void -callR(Context* con, unsigned size UNUSED, Assembler::Register* target) -{ - assert(con, size == TargetBytesPerWord); - emit(con, blx(target->low)); -} - -void -callC(Context* con, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(con, size == TargetBytesPerWord); - - appendOffsetTask(con, target->value, offset(con)); - emit(con, bl(0)); -} - -void -longCallC(Context* con, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(con, size == TargetBytesPerWord); - - Assembler::Register tmp(4); - moveCR2(con, TargetBytesPerWord, target, &tmp, offset(con)); - callR(con, TargetBytesPerWord, &tmp); -} - -void -longJumpC(Context* con, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(con, size == TargetBytesPerWord); - - Assembler::Register tmp(4); // a non-arg reg that we don't mind clobbering - moveCR2(con, TargetBytesPerWord, target, &tmp, offset(con)); - jumpR(con, TargetBytesPerWord, &tmp); -} - -void -jumpC(Context* con, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(con, size == TargetBytesPerWord); - - appendOffsetTask(con, target->value, offset(con)); - emit(con, b(0)); -} - -void -return_(Context* con) -{ - emit(con, bx(LinkRegister)); -} - -void -trap(Context* con) -{ - emit(con, bkpt(0)); -} - -void -memoryBarrier(Context*) {} - -// END OPERATION COMPILERS - -unsigned -argumentFootprint(unsigned footprint) -{ - return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); -} - -void -nextFrame(ArchitectureContext* con, uint32_t* start, unsigned size UNUSED, - unsigned footprint, void* link, bool, - unsigned targetParameterFootprint UNUSED, void** ip, void** stack) -{ - assert(con, *ip >= start); - assert(con, *ip <= start + (size / TargetBytesPerWord)); - - uint32_t* instruction = static_cast(*ip); - - if ((*start >> 20) == 0xe59) { - // skip stack overflow check - start += 3; - } - - if (instruction <= start) { - *ip = link; - return; - } - - unsigned offset = footprint + FrameHeaderSize; - - if (instruction <= start + 2) { - *ip = link; - *stack = static_cast(*stack) + offset; - return; - } - - if (*instruction == 0xe12fff1e) { // return - *ip = link; - return; - } - - if (TailCalls) { - if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { - offset += argumentFootprint(targetParameterFootprint) - - StackAlignmentInWords; - } - - // check for post-non-tail-call stack adjustment of the form "add - // sp, sp, #offset": - if ((*instruction >> 12) == 0xe24dd) { - unsigned value = *instruction & 0xff; - unsigned rotation = (*instruction >> 8) & 0xf; - switch (rotation) { - case 0: offset -= value / TargetBytesPerWord; break; - case 15: offset -= value; break; - default: abort(con); - } - } - - // todo: check for and handle tail calls - } - - *ip = static_cast(*stack)[offset - 1]; - *stack = static_cast(*stack) + offset; -} - -void -populateTables(ArchitectureContext* con) -{ - const OperandType C = ConstantOperand; - const OperandType A = AddressOperand; - const OperandType R = RegisterOperand; - const OperandType M = MemoryOperand; - - OperationType* zo = con->operations; - UnaryOperationType* uo = con->unaryOperations; - BinaryOperationType* bo = con->binaryOperations; - TernaryOperationType* to = con->ternaryOperations; - BranchOperationType* bro = con->branchOperations; - - zo[Return] = return_; - zo[LoadBarrier] = memoryBarrier; - zo[StoreStoreBarrier] = memoryBarrier; - zo[StoreLoadBarrier] = memoryBarrier; - zo[Trap] = trap; - - uo[index(con, LongCall, C)] = CAST1(longCallC); - - uo[index(con, AlignedLongCall, C)] = CAST1(longCallC); - - uo[index(con, LongJump, C)] = CAST1(longJumpC); - - uo[index(con, AlignedLongJump, C)] = CAST1(longJumpC); - - uo[index(con, Jump, R)] = CAST1(jumpR); - uo[index(con, Jump, C)] = CAST1(jumpC); - - uo[index(con, AlignedJump, R)] = CAST1(jumpR); - uo[index(con, AlignedJump, C)] = CAST1(jumpC); - - uo[index(con, Call, C)] = CAST1(callC); - uo[index(con, Call, R)] = CAST1(callR); - - uo[index(con, AlignedCall, C)] = CAST1(callC); - uo[index(con, AlignedCall, R)] = CAST1(callR); - - bo[index(con, Move, R, R)] = CAST2(moveRR); - bo[index(con, Move, C, R)] = CAST2(moveCR); - bo[index(con, Move, C, M)] = CAST2(moveCM); - bo[index(con, Move, M, R)] = CAST2(moveMR); - bo[index(con, Move, R, M)] = CAST2(moveRM); - bo[index(con, Move, A, R)] = CAST2(moveAR); - - bo[index(con, MoveZ, R, R)] = CAST2(moveZRR); - bo[index(con, MoveZ, M, R)] = CAST2(moveZMR); - bo[index(con, MoveZ, C, R)] = CAST2(moveCR); - - bo[index(con, Negate, R, R)] = CAST2(negateRR); - - bo[index(con, FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); - bo[index(con, FloatNegate, R, R)] = CAST2(floatNegateRR); - bo[index(con, Float2Float, R, R)] = CAST2(float2FloatRR); - bo[index(con, Float2Int, R, R)] = CAST2(float2IntRR); - bo[index(con, Int2Float, R, R)] = CAST2(int2FloatRR); - bo[index(con, FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); - - to[index(con, Add, R)] = CAST3(addR); - - to[index(con, Subtract, R)] = CAST3(subR); - - to[index(con, Multiply, R)] = CAST3(multiplyR); - - to[index(con, FloatAdd, R)] = CAST3(floatAddR); - to[index(con, FloatSubtract, R)] = CAST3(floatSubtractR); - to[index(con, FloatMultiply, R)] = CAST3(floatMultiplyR); - to[index(con, FloatDivide, R)] = CAST3(floatDivideR); - - to[index(con, ShiftLeft, R)] = CAST3(shiftLeftR); - to[index(con, ShiftLeft, C)] = CAST3(shiftLeftC); - - to[index(con, ShiftRight, R)] = CAST3(shiftRightR); - to[index(con, ShiftRight, C)] = CAST3(shiftRightC); - - to[index(con, UnsignedShiftRight, R)] = CAST3(unsignedShiftRightR); - to[index(con, UnsignedShiftRight, C)] = CAST3(unsignedShiftRightC); - - to[index(con, And, R)] = CAST3(andR); - to[index(con, And, C)] = CAST3(andC); - - to[index(con, Or, R)] = CAST3(orR); - - to[index(con, Xor, R)] = CAST3(xorR); - - bro[branchIndex(con, R, R)] = CAST_BRANCH(branchRR); - bro[branchIndex(con, C, R)] = CAST_BRANCH(branchCR); - bro[branchIndex(con, C, M)] = CAST_BRANCH(branchCM); - bro[branchIndex(con, R, M)] = CAST_BRANCH(branchRM); -} - -class MyArchitecture: public Assembler::Architecture { - public: - MyArchitecture(System* system): con(system), referenceCount(0) { - populateTables(&con); - } - - virtual unsigned floatRegisterSize() { - return vfpSupported() ? 8 : 0; - } - - virtual uint32_t generalRegisterMask() { - return GPR_MASK; - } - - virtual uint32_t floatRegisterMask() { - return vfpSupported() ? FPR_MASK : 0; - } - - virtual int scratch() { - return 5; - } - - virtual int stack() { - return StackRegister; - } - - virtual int thread() { - return ThreadRegister; - } - - virtual int returnLow() { - return 0; - } - - virtual int returnHigh() { - return 1; - } - - virtual int virtualCallTarget() { - return 4; - } - - virtual int virtualCallIndex() { - return 3; - } - - virtual bool bigEndian() { - return false; - } - - virtual uintptr_t maximumImmediateJump() { - return 0x1FFFFFF; - } - - virtual bool reserved(int register_) { - switch (register_) { - case LinkRegister: - case StackRegister: - case ThreadRegister: - case ProgramCounter: - return true; - - default: - return false; - } - } - - virtual unsigned frameFootprint(unsigned footprint) { - return max(footprint, StackAlignmentInWords); - } - - virtual unsigned argumentFootprint(unsigned footprint) { - return ::argumentFootprint(footprint); - } - - virtual bool argumentAlignment() { -#ifdef __APPLE__ - return false; -#else - return true; -#endif - } - - virtual bool argumentRegisterAlignment() { -#ifdef __APPLE__ - return false; -#else - return true; -#endif - } - - virtual unsigned argumentRegisterCount() { - return 4; - } - - virtual int argumentRegister(unsigned index) { - assert(&con, index < argumentRegisterCount()); - - return index; - } - - virtual bool hasLinkRegister() { - return true; - } - - 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, - void* returnAddress, - void* newTarget) - { - switch (op) { - case Call: - case Jump: - case AlignedCall: - case AlignedJump: { - updateOffset(con.s, static_cast(returnAddress) - 4, - reinterpret_cast(newTarget)); - } break; - - case LongCall: - case LongJump: - case AlignedLongCall: - case AlignedLongJump: { - uint32_t* p = static_cast(returnAddress) - 2; - *reinterpret_cast(p + (((*p & PoolOffsetMask) + 8) / 4)) - = newTarget; - } break; - - default: abort(&con); - } - } - - virtual unsigned constantCallSize() { - return 4; - } - - virtual void setConstant(void* dst, uint64_t constant) { - *static_cast(dst) = constant; - } - - virtual unsigned alignFrameSize(unsigned sizeInWords) { - return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) - - FrameHeaderSize; - } - - virtual void nextFrame(void* start, unsigned size, unsigned footprint, - void* link, bool mostRecent, - unsigned targetParameterFootprint, void** ip, - void** stack) - { - ::nextFrame(&con, static_cast(start), size, footprint, link, - mostRecent, targetParameterFootprint, ip, stack); - } - - virtual void* frameIp(void* stack) { - return stack ? static_cast(stack)[returnAddressOffset()] : 0; - } - - virtual unsigned frameHeaderSize() { - return FrameHeaderSize; - } - - virtual unsigned frameReturnAddressSize() { - return 0; - } - - virtual unsigned frameFooterSize() { - return 0; - } - - virtual int returnAddressOffset() { - return -1; - } - - virtual int framePointerOffset() { - return 0; - } - - virtual BinaryOperation hasBinaryIntrinsic(Thread*, object) { - return NoBinaryOperation; - } - - virtual TernaryOperation hasTernaryIntrinsic(Thread*, object) { - return NoTernaryOperation; - } - - virtual bool alwaysCondensed(BinaryOperation) { - return false; - } - - virtual bool alwaysCondensed(TernaryOperation) { - return false; - } - - virtual void plan - (UnaryOperation, - unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, - bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *aRegisterMask = ~static_cast(0); - *thunk = false; - } - - virtual void planSource - (BinaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned bSize, bool* thunk) - { - *thunk = false; - *aTypeMask = ~0; - *aRegisterMask = GPR_MASK64; - - switch (op) { - case Negate: - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = GPR_MASK64; - break; - - case Absolute: - *thunk = true; - break; - - case FloatAbsolute: - case FloatSquareRoot: - case FloatNegate: - case Float2Float: - if (vfpSupported()) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = FPR_MASK64; - } else { - *thunk = true; - } - break; - - case Float2Int: - // todo: Java requires different semantics than SSE for - // converting floats to integers, we we need to either use - // thunks or produce inline machine code which handles edge - // cases properly. - if (false && vfpSupported() && bSize == 4) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = FPR_MASK64; - } else { - *thunk = true; - } - break; - - case Int2Float: - if (vfpSupported() && aSize == 4) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = GPR_MASK64; - } else { - *thunk = true; - } - break; - - default: - break; - } - } - - virtual void planDestination - (BinaryOperation op, - unsigned, uint8_t aTypeMask, uint64_t, - unsigned , uint8_t* bTypeMask, uint64_t* bRegisterMask) - { - *bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *bRegisterMask = GPR_MASK64; - - switch (op) { - case Negate: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = GPR_MASK64; - break; - - case FloatAbsolute: - case FloatSquareRoot: - case FloatNegate: - case Float2Float: - case Int2Float: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = FPR_MASK64; - break; - - case Float2Int: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = GPR_MASK64; - break; - - case Move: - if (!(aTypeMask & 1 << RegisterOperand)) { - *bTypeMask = 1 << RegisterOperand; - } - break; - - default: - break; - } - } - - virtual void planMove - (unsigned, uint8_t* srcTypeMask, uint64_t* srcRegisterMask, - uint8_t* tmpTypeMask, uint64_t* tmpRegisterMask, - uint8_t dstTypeMask, uint64_t dstRegisterMask) - { - *srcTypeMask = ~0; - *srcRegisterMask = ~static_cast(0); - - *tmpTypeMask = 0; - *tmpRegisterMask = 0; - - if (dstTypeMask & (1 << MemoryOperand)) { - // can't move directly from memory or constant to memory - *srcTypeMask = 1 << RegisterOperand; - *tmpTypeMask = 1 << RegisterOperand; - *tmpRegisterMask = GPR_MASK64; - } else if (vfpSupported() && - dstTypeMask & 1 << RegisterOperand && - dstRegisterMask & FPR_MASK) { - *srcTypeMask = *tmpTypeMask = 1 << RegisterOperand | - 1 << MemoryOperand; - *tmpRegisterMask = ~static_cast(0); - } - } - - virtual void planSource - (TernaryOperation op, - unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned bSize, uint8_t* bTypeMask, uint64_t* bRegisterMask, - unsigned, bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *aRegisterMask = GPR_MASK64; - - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = GPR_MASK64; - - *thunk = false; - - switch (op) { - case ShiftLeft: - case ShiftRight: - case UnsignedShiftRight: - if (bSize == 8) *aTypeMask = *bTypeMask = (1 << RegisterOperand); - break; - - case Add: - case Subtract: - case Or: - case Xor: - case Multiply: - *aTypeMask = *bTypeMask = (1 << RegisterOperand); - break; - - case Divide: - case Remainder: - case FloatRemainder: - *thunk = true; - break; - - case FloatAdd: - case FloatSubtract: - case FloatMultiply: - case FloatDivide: - if (vfpSupported()) { - *aTypeMask = *bTypeMask = (1 << RegisterOperand); - *aRegisterMask = *bRegisterMask = FPR_MASK64; - } else { - *thunk = true; - } - break; - - case JumpIfFloatEqual: - case JumpIfFloatNotEqual: - case JumpIfFloatLess: - case JumpIfFloatGreater: - case JumpIfFloatLessOrEqual: - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatLessOrUnordered: - case JumpIfFloatGreaterOrUnordered: - case JumpIfFloatLessOrEqualOrUnordered: - case JumpIfFloatGreaterOrEqualOrUnordered: - if (vfpSupported()) { - *aTypeMask = *bTypeMask = (1 << RegisterOperand); - *aRegisterMask = *bRegisterMask = FPR_MASK64; - } else { - *thunk = true; - } - break; - - default: - break; - } - } - - virtual void planDestination - (TernaryOperation op, - unsigned, uint8_t, uint64_t, - unsigned, uint8_t, const uint64_t bRegisterMask, - unsigned, uint8_t* cTypeMask, uint64_t* cRegisterMask) - { - if (isBranch(op)) { - *cTypeMask = (1 << ConstantOperand); - *cRegisterMask = 0; - } else { - *cTypeMask = (1 << RegisterOperand); - *cRegisterMask = bRegisterMask; - } - } - - virtual void acquire() { - ++ referenceCount; - } - - virtual void release() { - if (-- referenceCount == 0) { - con.s->free(this); - } - } - - ArchitectureContext con; - unsigned referenceCount; -}; - -class MyAssembler: public Assembler { - public: - MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): - con(s, a, zone), arch_(arch) - { } - - virtual void setClient(Client* client) { - assert(&con, con.client == 0); - con.client = client; - } - - virtual Architecture* arch() { - return arch_; - } - - virtual void checkStackOverflow(uintptr_t handler, - unsigned stackLimitOffsetFromThread) - { - Register stack(StackRegister); - Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); - Constant handlerConstant(new(con.zone) ResolvedPromise(handler)); - branchRM(&con, JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, - &handlerConstant); - } - - virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) { - Register link(LinkRegister); - Memory linkDst(ThreadRegister, ipOffset); - moveRM(&con, TargetBytesPerWord, &link, TargetBytesPerWord, &linkDst); - - Register stack(StackRegister); - Memory stackDst(ThreadRegister, stackOffset); - moveRM(&con, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); - } - - virtual void pushFrame(unsigned argumentCount, ...) { - struct { - unsigned size; - OperandType type; - Operand* operand; - } arguments[argumentCount]; - - va_list a; va_start(a, argumentCount); - unsigned footprint = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - arguments[i].size = va_arg(a, unsigned); - arguments[i].type = static_cast(va_arg(a, int)); - arguments[i].operand = va_arg(a, Operand*); - footprint += ceiling(arguments[i].size, TargetBytesPerWord); - } - va_end(a); - - allocateFrame(arch_->alignFrameSize(footprint)); - - unsigned offset = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - if (i < arch_->argumentRegisterCount()) { - Register dst(arch_->argumentRegister(i)); - - apply(Move, - arguments[i].size, arguments[i].type, arguments[i].operand, - pad(arguments[i].size, TargetBytesPerWord), RegisterOperand, - &dst); - - offset += ceiling(arguments[i].size, TargetBytesPerWord); - } else { - Memory dst(StackRegister, offset * TargetBytesPerWord); - - apply(Move, - arguments[i].size, arguments[i].type, arguments[i].operand, - pad(arguments[i].size, TargetBytesPerWord), MemoryOperand, &dst); - - offset += ceiling(arguments[i].size, TargetBytesPerWord); - } - } - } - - virtual void allocateFrame(unsigned footprint) { - footprint += FrameHeaderSize; - - // larger frames may require multiple subtract/add instructions - // to allocate/deallocate, and nextFrame will need to be taught - // how to handle them: - assert(&con, footprint < 256); - - Register stack(StackRegister); - ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); - Constant footprintConstant(&footprintPromise); - subC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); - - Register returnAddress(LinkRegister); - Memory returnAddressDst - (StackRegister, (footprint - 1) * TargetBytesPerWord); - moveRM(&con, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, - &returnAddressDst); - } - - virtual void adjustFrame(unsigned difference) { - Register stack(StackRegister); - ResolvedPromise differencePromise(difference * TargetBytesPerWord); - Constant differenceConstant(&differencePromise); - subC(&con, TargetBytesPerWord, &differenceConstant, &stack, &stack); - } - - virtual void popFrame(unsigned footprint) { - footprint += FrameHeaderSize; - - Register returnAddress(LinkRegister); - Memory returnAddressSrc - (StackRegister, (footprint - 1) * TargetBytesPerWord); - moveMR(&con, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, - &returnAddress); - - Register stack(StackRegister); - ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); - Constant footprintConstant(&footprintPromise); - addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); - } - - virtual void popFrameForTailCall(unsigned footprint, - int offset, - int returnAddressSurrogate, - int framePointerSurrogate UNUSED) - { - assert(&con, framePointerSurrogate == NoRegister); - - if (TailCalls) { - if (offset) { - footprint += FrameHeaderSize; - - Register link(LinkRegister); - Memory returnAddressSrc - (StackRegister, (footprint - 1) * TargetBytesPerWord); - moveMR(&con, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, - &link); - - Register stack(StackRegister); - ResolvedPromise footprintPromise - ((footprint - offset) * TargetBytesPerWord); - Constant footprintConstant(&footprintPromise); - addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); - - if (returnAddressSurrogate != NoRegister) { - assert(&con, offset > 0); - - Register ras(returnAddressSurrogate); - Memory dst(StackRegister, (offset - 1) * TargetBytesPerWord); - moveRM(&con, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); - } - } else { - popFrame(footprint); - } - } else { - abort(&con); - } - } - - virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, - unsigned argumentFootprint) - { - popFrame(frameFootprint); - - assert(&con, argumentFootprint >= StackAlignmentInWords); - assert(&con, (argumentFootprint % StackAlignmentInWords) == 0); - - unsigned offset; - if (TailCalls and argumentFootprint > StackAlignmentInWords) { - offset = argumentFootprint - StackAlignmentInWords; - - Register stack(StackRegister); - ResolvedPromise adjustmentPromise(offset * TargetBytesPerWord); - Constant adjustment(&adjustmentPromise); - addC(&con, TargetBytesPerWord, &adjustment, &stack, &stack); - } else { - offset = 0; - } - - return_(&con); - } - - virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, - unsigned stackOffsetFromThread) - { - popFrame(frameFootprint); - - Register stack(StackRegister); - Memory newStackSrc(ThreadRegister, stackOffsetFromThread); - moveMR(&con, TargetBytesPerWord, &newStackSrc, TargetBytesPerWord, &stack); - - return_(&con); - } - - virtual void apply(Operation op) { - arch_->con.operations[op](&con); - } - - virtual void apply(UnaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand) - { - arch_->con.unaryOperations[index(&(arch_->con), op, aType)] - (&con, aSize, aOperand); - } - - virtual void apply(BinaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand) - { - arch_->con.binaryOperations[index(&(arch_->con), op, aType, bType)] - (&con, aSize, aOperand, bSize, bOperand); - } - - virtual void apply(TernaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType UNUSED, - Operand* bOperand, - unsigned cSize UNUSED, OperandType cType UNUSED, - Operand* cOperand) - { - if (isBranch(op)) { - assert(&con, aSize == bSize); - assert(&con, cSize == TargetBytesPerWord); - assert(&con, cType == ConstantOperand); - - arch_->con.branchOperations[branchIndex(&(arch_->con), aType, bType)] - (&con, op, aSize, aOperand, bOperand, cOperand); - } else { - assert(&con, bSize == cSize); - assert(&con, bType == RegisterOperand); - assert(&con, cType == RegisterOperand); - - arch_->con.ternaryOperations[index(&(arch_->con), op, aType)] - (&con, bSize, aOperand, bOperand, cOperand); - } - } - - virtual void setDestination(uint8_t* dst) { - con.result = dst; - } - - virtual void write() { - uint8_t* dst = con.result; - unsigned dstOffset = 0; - for (MyBlock* b = con.firstBlock; b; b = b->next) { - if (DebugPool) { - fprintf(stderr, "write block %p\n", b); - } - - unsigned blockOffset = 0; - for (PoolEvent* e = b->poolEventHead; e; e = e->next) { - unsigned size = e->offset - blockOffset; - memcpy(dst + dstOffset, con.code.data + b->offset + blockOffset, size); - blockOffset = e->offset; - dstOffset += size; - - unsigned poolSize = 0; - for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { - if (DebugPool) { - fprintf(stderr, "visit pool offset %p %d in block %p\n", - o, o->offset, b); - } - - unsigned entry = dstOffset + poolSize; - - if (needJump(b)) { - entry += TargetBytesPerWord; - } - - o->entry->address = dst + entry; - - unsigned instruction = o->block->start - + padding(o->block, o->offset) + o->offset; - - int32_t v = (entry - 8) - instruction; - expect(&con, v == (v & PoolOffsetMask)); - - int32_t* p = reinterpret_cast(dst + instruction); - *p = (v & PoolOffsetMask) | ((~PoolOffsetMask) & *p); - - poolSize += TargetBytesPerWord; - } - - bool jump = needJump(b); - if (jump) { - write4 - (dst + dstOffset, ::b((poolSize + TargetBytesPerWord - 8) >> 2)); - } - - dstOffset += poolSize + (jump ? TargetBytesPerWord : 0); - } - - unsigned size = b->size - blockOffset; - - memcpy(dst + dstOffset, - con.code.data + b->offset + blockOffset, - size); - - dstOffset += size; - } - - for (Task* t = con.tasks; t; t = t->next) { - t->run(&con); - } - - for (ConstantPoolEntry* e = con.constantPool; e; e = e->next) { - if (e->constant->resolved()) { - *static_cast(e->address) = e->constant->value(); - } else { - new (e->constant->listen(sizeof(ConstantPoolListener))) - ConstantPoolListener(con.s, static_cast(e->address), - e->callOffset - ? dst + e->callOffset->value() + 8 - : 0); - } -// fprintf(stderr, "constant %p at %p\n", reinterpret_cast(e->constant->value()), e->address); - } - } - - virtual Promise* offset(bool forTrace) { - return ::offset(&con, forTrace); - } - - virtual Block* endBlock(bool startNew) { - MyBlock* b = con.lastBlock; - b->size = con.code.length() - b->offset; - if (startNew) { - con.lastBlock = new (con.zone) MyBlock(&con, con.code.length()); - } else { - con.lastBlock = 0; - } - return b; - } - - virtual void endEvent() { - MyBlock* b = con.lastBlock; - unsigned thisEventOffset = con.code.length() - b->offset; - if (b->poolOffsetHead) { - int32_t v = (thisEventOffset + TargetBytesPerWord - 8) - - b->poolOffsetHead->offset; - - if (v > 0 and v != (v & PoolOffsetMask)) { - appendPoolEvent - (&con, b, b->lastEventOffset, b->poolOffsetHead, - b->lastPoolOffsetTail); - - if (DebugPool) { - for (PoolOffset* o = b->poolOffsetHead; - o != b->lastPoolOffsetTail->next; o = o->next) - { - fprintf(stderr, - "in endEvent, include %p %d in pool event %p at offset %d " - "in block %p\n", - o, o->offset, b->poolEventTail, b->lastEventOffset, b); - } - } - - b->poolOffsetHead = b->lastPoolOffsetTail->next; - b->lastPoolOffsetTail->next = 0; - if (b->poolOffsetHead == 0) { - b->poolOffsetTail = 0; - } - } - } - b->lastEventOffset = thisEventOffset; - b->lastPoolOffsetTail = b->poolOffsetTail; - } - - virtual unsigned length() { - return con.code.length(); - } - - virtual unsigned footerSize() { - return 0; - } - - virtual void dispose() { - con.code.dispose(); - } - - Context con; - MyArchitecture* arch_; -}; - -} // namespace - -namespace vm { - -Assembler::Architecture* -makeArchitecture(System* system, bool) -{ - return new (allocate(system, sizeof(MyArchitecture))) MyArchitecture(system); -} - -Assembler* -makeAssembler(System* system, Allocator* allocator, Zone* zone, - Assembler::Architecture* architecture) -{ - return new(zone) MyAssembler(system, allocator, zone, - static_cast(architecture)); -} - -} // namespace vm diff --git a/src/arm.masm b/src/arm.masm new file mode 100644 index 0000000000..e7bd2d864c --- /dev/null +++ b/src/arm.masm @@ -0,0 +1,89 @@ +; Copyright (c) 2008-2011, 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. +; +; ORIGIN: https://github.com/gkvas/avian/tree/wince + + AREA text, CODE, ARM + + EXPORT vmNativeCall +vmNativeCall + ; arguments: + ; r0 -> r4 : function + ; r1 -> r5 : stackTotal + ; r2 : memoryTable + ; r3 : memoryCount + ; [sp, #0] -> r6 : gprTable + + mov ip, sp ; save stack frame + stmfd sp!, {r4-r6, lr} ; save clobbered non-volatile regs + + ; mv args into non-volatile regs + mov r4, r0 + mov r5, r1 + ldr r6, [ip] + + ; setup stack arguments if necessary + sub sp, sp, r5 ; allocate stack + mov ip, sp +loop + tst r3, r3 + ldrne r0, [r2], #4 + strne r0, [ip], #4 + subne r3, r3, #4 + bne loop + + ; setup argument registers if necessary + tst r6, r6 + ldmneia r6, {r0-r3} + + blx r4 ; call function + add sp, sp, r5 ; deallocate stack + + ldmfd sp!, {r4-r6, pc} ; restore non-volatile regs and return + + EXPORT vmJump +vmJump + mov lr, r0 + ldr r0, [sp] + ldr r1, [sp, #4] + mov sp, r2 + mov r8, r3 + bx lr + +CHECKPOINT_THREAD EQU 4 +CHECKPOINT_STACK EQU 24 + + EXPORT vmRun +vmRun + ; r0: function + ; r1: arguments + ; r2: checkpoint + stmfd sp!, {r4-r11, lr} + ; align stack + sub sp, sp, #12 + + str sp, [r2, #CHECKPOINT_STACK] + + mov r12, r0 + ldr r0, [r2, #CHECKPOINT_THREAD] + + blx r12 + + EXPORT vmRun_returnAddress +vmRun_returnAddress + add sp, sp, #12 + ldmfd sp!, {r4-r11, lr} + bx lr + + EXPORT vmTrap +vmTrap + bkpt 3 + + END \ No newline at end of file diff --git a/src/assembler.h b/src/assembler.h deleted file mode 100644 index d2ba2caf5d..0000000000 --- a/src/assembler.h +++ /dev/null @@ -1,478 +0,0 @@ -/* Copyright (c) 2008-2012, 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. */ - -#ifndef ASSEMBLER_H -#define ASSEMBLER_H - -#include "system.h" -#include "zone.h" - -namespace vm { - -#ifdef AVIAN_TAILS -const bool TailCalls = true; -#else -const bool TailCalls = false; -#endif - -#if (defined AVIAN_USE_FRAME_POINTER) || (defined ARCH_powerpc) -const bool UseFramePointer = true; -#else -const bool UseFramePointer = false; -#endif - -enum Operation { - Return, - LoadBarrier, - StoreStoreBarrier, - StoreLoadBarrier, - Trap -}; - -const unsigned OperationCount = Trap + 1; - -enum UnaryOperation { - Call, - LongCall, - AlignedLongCall, - AlignedCall, - Jump, - LongJump, - AlignedLongJump, - AlignedJump, - - NoUnaryOperation = -1 -}; - -const unsigned UnaryOperationCount = AlignedJump + 1; - -enum BinaryOperation { - Move, - MoveLow, - MoveHigh, - MoveZ, - Negate, - FloatNegate, - Float2Float, - Float2Int, - Int2Float, - FloatSquareRoot, - FloatAbsolute, - Absolute, - - NoBinaryOperation = -1 -}; - -const unsigned BinaryOperationCount = Absolute + 1; - -enum TernaryOperation { - Add, - Subtract, - Multiply, - Divide, - Remainder, - ShiftLeft, - ShiftRight, - UnsignedShiftRight, - And, - Or, - Xor, - FloatAdd, - FloatSubtract, - FloatMultiply, - FloatDivide, - FloatRemainder, - FloatMax, - FloatMin, - JumpIfLess, - JumpIfGreater, - JumpIfLessOrEqual, - JumpIfGreaterOrEqual, - JumpIfEqual, - JumpIfNotEqual, - JumpIfFloatEqual, - JumpIfFloatNotEqual, - JumpIfFloatLess, - JumpIfFloatGreater, - JumpIfFloatLessOrEqual, - JumpIfFloatGreaterOrEqual, - JumpIfFloatLessOrUnordered, - JumpIfFloatGreaterOrUnordered, - JumpIfFloatLessOrEqualOrUnordered, - JumpIfFloatGreaterOrEqualOrUnordered, - - NoTernaryOperation = -1 -}; - -const unsigned TernaryOperationCount -= JumpIfFloatGreaterOrEqualOrUnordered + 1; - -const unsigned NonBranchTernaryOperationCount = FloatMin + 1; -const unsigned BranchOperationCount -= JumpIfFloatGreaterOrEqualOrUnordered - FloatMin; - -enum OperandType { - ConstantOperand, - AddressOperand, - RegisterOperand, - MemoryOperand -}; - -enum ValueType { - ValueGeneral, - ValueFloat -}; - -const unsigned OperandTypeCount = MemoryOperand + 1; - -const int NoRegister = -1; - -class Promise { - public: - class Listener { - public: - virtual bool resolve(int64_t value, void** location) = 0; - - Listener* next; - }; - - virtual int64_t value() = 0; - virtual bool resolved() = 0; - virtual Listener* listen(unsigned) { return 0; } -}; - -class ResolvedPromise: public Promise { - public: - ResolvedPromise(int64_t value): value_(value) { } - - virtual int64_t value() { - return value_; - } - - virtual bool resolved() { - return true; - } - - int64_t value_; -}; - -class ShiftMaskPromise: public Promise { - public: - ShiftMaskPromise(Promise* base, unsigned shift, int64_t mask): - base(base), shift(shift), mask(mask) - { } - - virtual int64_t value() { - return (base->value() >> shift) & mask; - } - - virtual bool resolved() { - return base->resolved(); - } - - Promise* base; - unsigned shift; - int64_t mask; -}; - -class CombinedPromise: public Promise { - public: - CombinedPromise(Promise* low, Promise* high): - low(low), high(high) - { } - - virtual int64_t value() { - return low->value() | (high->value() << 32); - } - - virtual bool resolved() { - return low->resolved() and high->resolved(); - } - - Promise* low; - Promise* high; -}; - -class OffsetPromise: public Promise { - public: - OffsetPromise(Promise* base, int64_t offset): - base(base), offset(offset) - { } - - virtual int64_t value() { - return base->value() + offset; - } - - virtual bool resolved() { - return base->resolved(); - } - - Promise* base; - int64_t offset; -}; - -class ListenPromise: public Promise { - public: - ListenPromise(System* s, Allocator* allocator): - s(s), allocator(allocator), listener(0) - { } - - virtual int64_t value() { - abort(s); - } - - virtual bool resolved() { - return false; - } - - virtual Listener* listen(unsigned sizeInBytes) { - Listener* l = static_cast(allocator->allocate(sizeInBytes)); - l->next = listener; - listener = l; - return l; - } - - System* s; - Allocator* allocator; - Listener* listener; - Promise* promise; -}; - -class DelayedPromise: public ListenPromise { - public: - DelayedPromise(System* s, Allocator* allocator, Promise* basis, - DelayedPromise* next): - ListenPromise(s, allocator), basis(basis), next(next) - { } - - virtual int64_t value() { - abort(s); - } - - virtual bool resolved() { - return false; - } - - virtual Listener* listen(unsigned sizeInBytes) { - Listener* l = static_cast(allocator->allocate(sizeInBytes)); - l->next = listener; - listener = l; - return l; - } - - Promise* basis; - DelayedPromise* next; -}; - -class Assembler { - public: - class Operand { }; - - class Constant: public Operand { - public: - Constant(Promise* value): value(value) { } - - Promise* value; - }; - - class Address: public Operand { - public: - Address(Promise* address): address(address) { } - - Promise* address; - }; - - class Register: public Operand { - public: - Register(int low, int high = NoRegister): low(low), high(high) { } - - int low; - int high; - }; - - class Memory: public Operand { - public: - Memory(int base, int offset, int index = NoRegister, unsigned scale = 1): - base(base), offset(offset), index(index), scale(scale) - { } - - int base; - int offset; - int index; - unsigned scale; - }; - - class Client { - public: - virtual int acquireTemporary - (uint32_t mask = ~static_cast(0)) = 0; - virtual void releaseTemporary(int r) = 0; - - virtual void save(int r) = 0; - }; - - class Block { - public: - virtual unsigned resolve(unsigned start, Block* next) = 0; - }; - - class Architecture { - public: - virtual unsigned floatRegisterSize() = 0; - - virtual uint32_t generalRegisterMask() = 0; - virtual uint32_t floatRegisterMask() = 0; - - virtual int scratch() = 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 uintptr_t maximumImmediateJump() = 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 bool argumentAlignment() = 0; - virtual bool argumentRegisterAlignment() = 0; - virtual unsigned argumentRegisterCount() = 0; - virtual int argumentRegister(unsigned index) = 0; - - virtual bool hasLinkRegister() = 0; - - virtual unsigned stackAlignmentInWords() = 0; - - virtual bool matchCall(void* returnAddress, void* target) = 0; - - virtual void updateCall(UnaryOperation op, void* returnAddress, - void* newTarget) = 0; - - virtual void setConstant(void* dst, uint64_t constant) = 0; - - virtual unsigned alignFrameSize(unsigned sizeInWords) = 0; - - virtual void nextFrame(void* start, unsigned size, unsigned footprint, - void* link, bool mostRecent, - unsigned targetParameterFootprint, void** ip, - void** stack) = 0; - virtual void* frameIp(void* stack) = 0; - virtual unsigned frameHeaderSize() = 0; - virtual unsigned frameReturnAddressSize() = 0; - virtual unsigned frameFooterSize() = 0; - virtual int returnAddressOffset() = 0; - virtual int framePointerOffset() = 0; - - virtual void plan - (UnaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - bool* thunk) = 0; - - virtual void planSource - (BinaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned bSize, bool* thunk) = 0; - - virtual void planDestination - (BinaryOperation op, - unsigned aSize, uint8_t aTypeMask, uint64_t aRegisterMask, - unsigned bSize, uint8_t* bTypeMask, uint64_t* bRegisterMask) = 0; - - virtual void planMove - (unsigned size, uint8_t* srcTypeMask, uint64_t* srcRegisterMask, - uint8_t* tmpTypeMask, uint64_t* tmpRegisterMask, - uint8_t dstTypeMask, uint64_t dstRegisterMask) = 0; - - virtual void planSource - (TernaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned bSize, uint8_t* bTypeMask, uint64_t* bRegisterMask, - unsigned cSize, bool* thunk) = 0; - - virtual void planDestination - (TernaryOperation op, - unsigned aSize, uint8_t aTypeMask, uint64_t aRegisterMask, - unsigned bSize, uint8_t bTypeMask, uint64_t bRegisterMask, - unsigned cSize, uint8_t* cTypeMask, uint64_t* cRegisterMask) = 0; - - virtual void acquire() = 0; - virtual void release() = 0; - }; - - virtual void setClient(Client* client) = 0; - - virtual Architecture* arch() = 0; - - virtual void checkStackOverflow(uintptr_t handler, - unsigned stackLimitOffsetFromThread) = 0; - virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) = 0; - virtual void pushFrame(unsigned argumentCount, ...) = 0; - virtual void allocateFrame(unsigned footprint) = 0; - virtual void adjustFrame(unsigned difference) = 0; - virtual void popFrame(unsigned footprint) = 0; - virtual void popFrameForTailCall(unsigned footprint, int offset, - int returnAddressSurrogate, - int framePointerSurrogate) = 0; - virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, - unsigned argumentFootprint) - = 0; - virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, - unsigned stackOffsetFromThread) - = 0; - - virtual void apply(Operation op) = 0; - - virtual void apply(UnaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand) = 0; - - virtual void apply(BinaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand) = 0; - - virtual void apply(TernaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand, - unsigned cSize, OperandType cType, Operand* cOperand) = 0; - - virtual void setDestination(uint8_t* dst) = 0; - - virtual void write() = 0; - - virtual Promise* offset(bool forTrace = false) = 0; - - virtual Block* endBlock(bool startNew) = 0; - - virtual void endEvent() = 0; - - virtual unsigned length() = 0; - - virtual unsigned footerSize() = 0; - - virtual void dispose() = 0; -}; - -Assembler::Architecture* -makeArchitecture(System* system, bool useNativeFeatures); - -Assembler* -makeAssembler(System* system, Allocator* allocator, Zone* zone, - Assembler::Architecture* architecture); - -} // namespace vm - -#endif//ASSEMBLER_H diff --git a/src/vector.h b/src/avian/alloc-vector.h similarity index 93% rename from src/vector.h rename to src/avian/alloc-vector.h index 6714573b0d..940f978bd2 100644 --- a/src/vector.h +++ b/src/avian/alloc-vector.h @@ -11,8 +11,13 @@ #ifndef VECTOR_H #define VECTOR_H -#include "system.h" -#include "target.h" +#include +#include "avian/target.h" + +#include + +#undef max +#undef min namespace vm { @@ -51,8 +56,8 @@ class Vector { if (position + space > capacity) { assert(s, minimumCapacity >= 0); - unsigned newCapacity = max - (position + space, max(minimumCapacity, capacity * 2)); + unsigned newCapacity = avian::util::max + (position + space, avian::util::max(minimumCapacity, capacity * 2)); uint8_t* newData = static_cast (allocator->allocate(newCapacity)); if (data) { diff --git a/src/allocator.h b/src/avian/allocator.h similarity index 98% rename from src/allocator.h rename to src/avian/allocator.h index 5bad9044b3..782ff88b5a 100644 --- a/src/allocator.h +++ b/src/avian/allocator.h @@ -11,7 +11,7 @@ #ifndef ALLOCATOR_H #define ALLOCATOR_H -#include "common.h" +#include "avian/common.h" namespace vm { diff --git a/src/arch.h b/src/avian/arch.h similarity index 97% rename from src/arch.h rename to src/avian/arch.h index 70a64fefdb..4190e2a752 100644 --- a/src/arch.h +++ b/src/avian/arch.h @@ -19,7 +19,7 @@ # undef interface #endif -#include "common.h" +#include "avian/common.h" extern "C" void NO_RETURN vmJump(void* address, void* frame, void* stack, void* thread, diff --git a/src/arm.h b/src/avian/arm.h similarity index 84% rename from src/arm.h rename to src/avian/arm.h index 8ae057d610..f8f4a633e7 100644 --- a/src/arm.h +++ b/src/avian/arm.h @@ -11,8 +11,9 @@ #ifndef ARM_H #define ARM_H -#include "types.h" -#include "common.h" +#include "avian/types.h" +#include "avian/common.h" +#include #ifdef __APPLE__ # include "libkern/OSAtomic.h" @@ -71,33 +72,57 @@ namespace vm { inline void trap() { +#ifdef _MSC_VER + __debugbreak(); +#else asm("bkpt"); +#endif } +#ifndef _MSC_VER inline void memoryBarrier() { asm("nop"); } +#endif inline void storeStoreMemoryBarrier() { +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else memoryBarrier(); +#endif } inline void storeLoadMemoryBarrier() { +#ifdef _MSC_VER + MemoryBarrier(); +#else memoryBarrier(); +#endif } inline void loadMemoryBarrier() { +#ifdef _MSC_VER + _ReadWriteBarrier(); +#else memoryBarrier(); +#endif } +#if !defined(AVIAN_AOT_ONLY) + +#if defined(__ANDROID__) +// http://code.google.com/p/android/issues/detail?id=1803 +extern "C" void __clear_cache (void *beg __attribute__((__unused__)), void *end __attribute__((__unused__))); +#endif inline void syncInstructionCache(const void* start, unsigned size) { @@ -112,6 +137,8 @@ syncInstructionCache(const void* start, unsigned size) #endif } +#endif // AVIAN_AOT_ONLY + #ifndef __APPLE__ typedef int (__kernel_cmpxchg_t)(int oldval, int newval, int *ptr); # define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) @@ -156,14 +183,14 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, unsigned vfpIndex = 0; unsigned vfpBackfillIndex UNUSED = 0; - uintptr_t stack[(argumentCount * 8) / BytesPerWord]; // is > argumentSize to account for padding + RUNTIME_ARRAY(uintptr_t, stack, (argumentCount * 8) / BytesPerWord); // is > argumentSize to account for padding unsigned stackIndex = 0; unsigned ai = 0; for (unsigned ati = 0; ati < argumentCount; ++ ati) { switch (argumentTypes[ati]) { case DOUBLE_TYPE: -#if (defined(__VFP_FP__) && !defined(__SOFTFP__)) && !defined(__QNX__) +#if defined(__ARM_PCS_VFP) { if (vfpIndex + Alignment <= VfpCount) { if (vfpIndex % Alignment) { @@ -179,7 +206,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, ++ stackIndex; } - memcpy(stack + stackIndex, arguments + ai, 8); + memcpy(RUNTIME_ARRAY_BODY(stack) + stackIndex, arguments + ai, 8); stackIndex += 8 / BytesPerWord; } ai += 8 / BytesPerWord; @@ -192,7 +219,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, } else if (vfpIndex < VfpCount) { vfpTable[vfpIndex++] = arguments[ai]; } else { - stack[stackIndex++] = arguments[ai]; + RUNTIME_ARRAY_BODY(stack)[stackIndex++] = arguments[ai]; } ++ ai; break; @@ -204,7 +231,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, and gprIndex + Alignment == GprCount) { gprTable[gprIndex++] = arguments[ai]; - stack[stackIndex++] = arguments[ai + 1]; + RUNTIME_ARRAY_BODY(stack)[stackIndex++] = arguments[ai + 1]; } else { if (gprIndex % Alignment) { ++gprIndex; @@ -219,7 +246,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, ++stackIndex; } - memcpy(stack + stackIndex, arguments + ai, 8); + memcpy(RUNTIME_ARRAY_BODY(stack) + stackIndex, arguments + ai, 8); stackIndex += 8 / BytesPerWord; } ai += 8 / BytesPerWord; @@ -229,7 +256,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, if (gprIndex < GprCount) { gprTable[gprIndex++] = arguments[ai]; } else { - stack[stackIndex++] = arguments[ai]; + RUNTIME_ARRAY_BODY(stack)[stackIndex++] = arguments[ai]; } ++ ai; } break; @@ -247,7 +274,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, unsigned stackSize = stackIndex*BytesPerWord + ((stackIndex & 1) << 2); return vmNativeCall - (function, stackSize, stack, stackIndex * BytesPerWord, + (function, stackSize, RUNTIME_ARRAY_BODY(stack), stackIndex * BytesPerWord, (gprIndex ? gprTable : 0), (vfpIndex ? vfpTable : 0), returnType); } diff --git a/src/bootimage.h b/src/avian/bootimage.h similarity index 91% rename from src/bootimage.h rename to src/avian/bootimage.h index 16f11ab379..b50379e8d8 100644 --- a/src/bootimage.h +++ b/src/avian/bootimage.h @@ -11,9 +11,12 @@ #ifndef BOOTIMAGE_H #define BOOTIMAGE_H -#include "common.h" -#include "target.h" -#include "machine.h" +#include "avian/common.h" +#include "java-common.h" +#include "avian/target.h" +#include "avian/machine.h" + +#include namespace vm { diff --git a/src/avian/classpath-common.h b/src/avian/classpath-common.h new file mode 100644 index 0000000000..1137f9d2c9 --- /dev/null +++ b/src/avian/classpath-common.h @@ -0,0 +1,677 @@ +/* Copyright (c) 2010-2012, 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. */ + +#ifndef CLASSPATH_COMMON_H +#define CLASSPATH_COMMON_H + +#include +#include + +using namespace avian::util; + +namespace vm { + +object +getTrace(Thread* t, unsigned skipCount) +{ + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t, int skipCount): + t(t), trace(0), skipCount(skipCount) + { } + + virtual bool visit(Processor::StackWalker* walker) { + if (skipCount == 0) { + object method = walker->method(); + if (isAssignableFrom + (t, type(t, Machine::ThrowableType), methodClass(t, method)) + and vm::strcmp(reinterpret_cast(""), + &byteArrayBody(t, methodName(t, method), 0)) + == 0) + { + return true; + } else { + trace = makeTrace(t, walker); + return false; + } + } else { + -- skipCount; + return true; + } + } + + Thread* t; + object trace; + unsigned skipCount; + } v(t, skipCount); + + t->m->processor->walkStack(t, &v); + + if (v.trace == 0) v.trace = makeObjectArray(t, 0); + + return v.trace; +} + +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 +arrayCopy(Thread* t, object src, int32_t srcOffset, object dst, + int32_t dstOffset, int32_t length) +{ + if (LIKELY(src and dst)) { + if (LIKELY(compatibleArrayTypes + (t, objectClass(t, src), objectClass(t, dst)))) + { + unsigned elementSize = classArrayElementSize(t, objectClass(t, src)); + + if (LIKELY(elementSize)) { + intptr_t sl = fieldAtOffset(src, BytesPerWord); + intptr_t dl = fieldAtOffset(dst, BytesPerWord); + if (LIKELY(length > 0)) { + if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl and + dstOffset >= 0 and dstOffset + length <= dl)) + { + uint8_t* sbody = &fieldAtOffset(src, ArrayBody); + uint8_t* dbody = &fieldAtOffset(dst, ArrayBody); + if (src == dst) { + memmove(dbody + (dstOffset * elementSize), + sbody + (srcOffset * elementSize), + length * elementSize); + } else { + memcpy(dbody + (dstOffset * elementSize), + sbody + (srcOffset * elementSize), + length * elementSize); + } + + if (classObjectMask(t, objectClass(t, dst))) { + mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length); + } + + return; + } else { + throwNew(t, Machine::IndexOutOfBoundsExceptionType); + } + } else { + return; + } + } + } + } else { + throwNew(t, Machine::NullPointerExceptionType); + return; + } + + throwNew(t, Machine::ArrayStoreExceptionType); +} + +void +runOnLoadIfFound(Thread* t, System::Library* library) +{ + void* p = library->resolve("JNI_OnLoad"); + +#ifdef PLATFORM_WINDOWS + if (p == 0) { + p = library->resolve("_JNI_OnLoad@8"); + if (p == 0) { + p = library->resolve("JNI_OnLoad@8"); + } + } +#endif + + if (p) { + jint (JNICALL * JNI_OnLoad)(Machine*, void*); + memcpy(&JNI_OnLoad, &p, sizeof(void*)); + JNI_OnLoad(t->m, 0); + } +} + +System::Library* +loadLibrary(Thread* t, const char* name) +{ + ACQUIRE(t, t->m->classLock); + + System::Library* last = t->m->libraries; + for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { + if (lib->name() and ::strcmp(lib->name(), name) == 0) { + // already loaded + return lib; + } + last = lib; + } + + System::Library* lib; + if (t->m->system->success(t->m->system->load(&lib, name))) { + last->setNext(lib); + return lib; + } else { + return 0; + } +} + +System::Library* +loadLibrary(Thread* t, const char* path, const char* name, bool mapName, + bool runOnLoad, bool throw_ = true) +{ + ACQUIRE(t, t->m->classLock); + + char* mappedName; + unsigned nameLength = strlen(name); + if (mapName) { + const char* builtins = findProperty(t, "avian.builtins"); + if (builtins) { + const char* s = builtins; + while (*s) { + if (::strncmp(s, name, nameLength) == 0 + and (s[nameLength] == ',' or s[nameLength] == 0)) + { + // library is built in to this executable + if (runOnLoad and not t->m->triedBuiltinOnLoad) { + t->m->triedBuiltinOnLoad = true; + // todo: release the classLock before calling this to + // avoid the possibility of deadlock: + runOnLoadIfFound(t, t->m->libraries); + } + return t->m->libraries; + } else { + while (*s and *s != ',') ++ s; + if (*s) ++ s; + } + } + } + + const char* prefix = t->m->system->libraryPrefix(); + const char* suffix = t->m->system->librarySuffix(); + unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix); + + mappedName = static_cast + (t->m->heap->allocate(mappedNameLength + 1)); + + snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix); + + name = mappedName; + nameLength = mappedNameLength; + } else { + mappedName = 0; + } + + THREAD_RESOURCE2 + (t, char*, mappedName, unsigned, nameLength, if (mappedName) { + t->m->heap->free(mappedName, nameLength + 1); + }); + + System::Library* lib = 0; + for (Tokenizer tokenizer(path, t->m->system->pathSeparator()); + tokenizer.hasMore();) + { + String token(tokenizer.next()); + + unsigned fullNameLength = token.length + 1 + nameLength; + THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1); + + snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1, + "%.*s/%s", token.length, token.text, name); + + lib = loadLibrary(t, RUNTIME_ARRAY_BODY(fullName)); + if (lib) break; + } + + if (lib == 0) { + lib = loadLibrary(t, name); + } + + if (lib) { + if (runOnLoad) { + runOnLoadIfFound(t, lib); + } + } else if (throw_) { + throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s", + name); + } + + return lib; +} + +object +clone(Thread* t, object o) +{ + PROTECT(t, o); + + 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); + // clear any object header flags: + setObjectClass(t, o, objectClass(t, o)); + } else if (instanceOf(t, type(t, Machine::CloneableType), o)) { + clone = make(t, class_); + memcpy(reinterpret_cast(clone) + 1, + reinterpret_cast(o) + 1, + size - BytesPerWord); + } else { + throwNew(t, Machine::CloneNotSupportedExceptionType, "%s", + &byteArrayBody(t, className(t, objectClass(t, o)), 0)); + } + + return clone; +} + +object +makeStackTraceElement(Thread* t, object e) +{ + PROTECT(t, e); + + object class_ = className(t, methodClass(t, traceElementMethod(t, e))); + PROTECT(t, class_); + + THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, class_)); + replace('/', '.', RUNTIME_ARRAY_BODY(s), + reinterpret_cast(&byteArrayBody(t, class_, 0))); + class_ = makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); + + object method = methodName(t, traceElementMethod(t, e)); + PROTECT(t, method); + + method = t->m->classpath->makeString + (t, method, 0, byteArrayLength(t, method) - 1); + + unsigned line = t->m->processor->lineNumber + (t, traceElementMethod(t, e), traceElementIp(t, e)); + + object file = classSourceFile(t, methodClass(t, traceElementMethod(t, e))); + file = file ? t->m->classpath->makeString + (t, file, 0, byteArrayLength(t, file) - 1) : 0; + + return makeStackTraceElement(t, class_, method, file, line); +} + +object +translateInvokeResult(Thread* t, unsigned returnCode, object o) +{ + switch (returnCode) { + case ByteField: + return makeByte(t, intValue(t, o)); + + case BooleanField: + return makeBoolean(t, intValue(t, o) != 0); + + case CharField: + return makeChar(t, intValue(t, o)); + + case ShortField: + return makeShort(t, intValue(t, o)); + + case FloatField: + return makeFloat(t, intValue(t, o)); + + case IntField: + case LongField: + case ObjectField: + case VoidField: + return o; + + case DoubleField: + return makeDouble(t, longValue(t, o)); + + default: + abort(t); + } +} + +object +resolveClassBySpec(Thread* t, object loader, const char* spec, + unsigned specLength) +{ + switch (*spec) { + case 'L': { + THREAD_RUNTIME_ARRAY(t, char, s, specLength - 1); + memcpy(RUNTIME_ARRAY_BODY(s), spec + 1, specLength - 2); + RUNTIME_ARRAY_BODY(s)[specLength - 2] = 0; + return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s)); + } + + case '[': { + THREAD_RUNTIME_ARRAY(t, char, s, specLength + 1); + memcpy(RUNTIME_ARRAY_BODY(s), spec, specLength); + RUNTIME_ARRAY_BODY(s)[specLength] = 0; + return resolveClass(t, loader, RUNTIME_ARRAY_BODY(s)); + } + + default: + return primitiveClass(t, *spec); + } +} + +object +resolveJType(Thread* t, object loader, const char* spec, unsigned specLength) +{ + return getJClass(t, resolveClassBySpec(t, loader, spec, specLength)); +} + +object +resolveParameterTypes(Thread* t, object loader, object spec, + unsigned* parameterCount, unsigned* returnTypeSpec) +{ + PROTECT(t, loader); + PROTECT(t, spec); + + object list = 0; + PROTECT(t, list); + + unsigned offset = 1; + unsigned count = 0; + while (byteArrayBody(t, spec, offset) != ')') { + switch (byteArrayBody(t, spec, offset)) { + case 'L': { + unsigned start = offset; + ++ offset; + while (byteArrayBody(t, spec, offset) != ';') ++ offset; + ++ offset; + + object type = resolveClassBySpec + (t, loader, reinterpret_cast(&byteArrayBody(t, spec, start)), + offset - start); + + list = makePair(t, type, list); + + ++ count; + } break; + + case '[': { + unsigned start = offset; + while (byteArrayBody(t, spec, offset) == '[') ++ offset; + switch (byteArrayBody(t, spec, offset)) { + case 'L': + ++ offset; + while (byteArrayBody(t, spec, offset) != ';') ++ offset; + ++ offset; + break; + + default: + ++ offset; + break; + } + + object type = resolveClassBySpec + (t, loader, reinterpret_cast(&byteArrayBody(t, spec, start)), + offset - start); + + list = makePair(t, type, list); + ++ count; + } break; + + default: + list = makePair + (t, primitiveClass(t, byteArrayBody(t, spec, offset)), list); + ++ offset; + ++ count; + break; + } + } + + *parameterCount = count; + *returnTypeSpec = offset + 1; + return list; +} + +object +resolveParameterJTypes(Thread* t, object loader, object spec, + unsigned* parameterCount, unsigned* returnTypeSpec) +{ + object list = resolveParameterTypes + (t, loader, spec, parameterCount, returnTypeSpec); + + PROTECT(t, list); + + object array = makeObjectArray + (t, type(t, Machine::JclassType), *parameterCount); + PROTECT(t, array); + + for (int i = *parameterCount - 1; i >= 0; --i) { + object c = getJClass(t, pairFirst(t, list)); + set(t, array, ArrayBody + (i * BytesPerWord), c); + list = pairSecond(t, list); + } + + return array; +} + +object +resolveExceptionJTypes(Thread* t, object loader, object addendum) +{ + if (addendum == 0 or methodAddendumExceptionTable(t, addendum) == 0) { + return makeObjectArray(t, type(t, Machine::JclassType), 0); + } + + PROTECT(t, loader); + PROTECT(t, addendum); + + object array = makeObjectArray + (t, type(t, Machine::JclassType), + shortArrayLength(t, methodAddendumExceptionTable(t, addendum))); + PROTECT(t, array); + + for (unsigned i = 0; i < shortArrayLength + (t, methodAddendumExceptionTable(t, addendum)); ++i) + { + uint16_t index = shortArrayBody + (t, methodAddendumExceptionTable(t, addendum), i) - 1; + + object o = singletonObject(t, addendumPool(t, addendum), index); + + if (objectClass(t, o) == type(t, Machine::ReferenceType)) { + o = resolveClass(t, loader, referenceName(t, o)); + + set(t, addendumPool(t, addendum), SingletonBody + (index * BytesPerWord), + o); + } + + o = getJClass(t, o); + + set(t, array, ArrayBody + (i * BytesPerWord), o); + } + + return array; +} + +object +invoke(Thread* t, object method, object instance, object args) +{ + PROTECT(t, method); + PROTECT(t, instance); + PROTECT(t, args); + + if (methodFlags(t, method) & ACC_STATIC) { + instance = 0; + } + + if ((args == 0 ? 0 : objectArrayLength(t, args)) + != methodParameterCount(t, method)) + { + throwNew(t, Machine::IllegalArgumentExceptionType); + } + + if (methodParameterCount(t, method)) { + PROTECT(t, method); + + unsigned specLength = byteArrayLength(t, methodSpec(t, method)); + THREAD_RUNTIME_ARRAY(t, char, spec, specLength); + memcpy(RUNTIME_ARRAY_BODY(spec), + &byteArrayBody(t, methodSpec(t, method), 0), specLength); + unsigned i = 0; + for (MethodSpecIterator it(t, RUNTIME_ARRAY_BODY(spec)); it.hasNext();) { + object type; + bool objectType = false; + const char* p = it.next(); + switch (*p) { + case 'Z': type = vm::type(t, Machine::BooleanType); break; + case 'B': type = vm::type(t, Machine::ByteType); break; + case 'S': type = vm::type(t, Machine::ShortType); break; + case 'C': type = vm::type(t, Machine::CharType); break; + case 'I': type = vm::type(t, Machine::IntType); break; + case 'F': type = vm::type(t, Machine::FloatType); break; + case 'J': type = vm::type(t, Machine::LongType); break; + case 'D': type = vm::type(t, Machine::DoubleType); break; + + case 'L': + case '[': { + objectType = true; + unsigned nameLength; + if (*p == 'L') { + ++ p; + nameLength = it.s - p; + } else { + nameLength = (it.s - p) + 1; + } + THREAD_RUNTIME_ARRAY(t, char, name, nameLength); + memcpy(RUNTIME_ARRAY_BODY(name), p, nameLength - 1); + RUNTIME_ARRAY_BODY(name)[nameLength - 1] = 0; + type = resolveClass + (t, classLoader(t, methodClass(t, method)), + RUNTIME_ARRAY_BODY(name)); + } break; + + default: + abort(); + } + + object arg = objectArrayBody(t, args, i++); + if ((arg == 0 and (not objectType)) + or (arg and (not instanceOf(t, type, arg)))) + { + // fprintf(stderr, "%s is not a %s\n", arg ? &byteArrayBody(t, className(t, objectClass(t, arg)), 0) : reinterpret_cast(""), &byteArrayBody(t, className(t, type), 0)); + + throwNew(t, Machine::IllegalArgumentExceptionType); + } + } + } + + unsigned returnCode = methodReturnCode(t, method); + + THREAD_RESOURCE0(t, { + if (t->exception) { + object exception = t->exception; + t->exception = makeThrowable + (t, Machine::InvocationTargetExceptionType, 0, 0, exception); + + set(t, t->exception, InvocationTargetExceptionTarget, + throwableCause(t, t->exception)); + } + }); + + object result; + if (args) { + result = t->m->processor->invokeArray(t, method, instance, args); + } else { + result = t->m->processor->invoke(t, method, instance); + } + + return translateInvokeResult(t, returnCode, result); +} + +// only safe to call during bootstrap when there's only one thread +// running: +void +intercept(Thread* t, object c, const char* name, const char* spec, + void* function, bool updateRuntimeData) +{ + object m = findMethodOrNull(t, c, name, spec); + if (m) { + PROTECT(t, m); + + object clone; + if (updateRuntimeData) { + clone = methodClone(t, m); + + // make clone private to prevent vtable updates at compilation + // time. Otherwise, our interception might be bypassed by calls + // through the vtable. + methodFlags(t, clone) |= ACC_PRIVATE; + } + + methodFlags(t, m) |= ACC_NATIVE; + + if (updateRuntimeData) { + object native = makeNativeIntercept(t, function, true, clone); + + PROTECT(t, native); + + object runtimeData = getMethodRuntimeData(t, m); + + set(t, runtimeData, MethodRuntimeDataNative, native); + } + } else { + // If we can't find the method, just ignore it, since ProGuard may + // have stripped it out as unused. Otherwise, the code below can + // be uncommented for debugging purposes. + + // fprintf(stderr, "unable to find %s%s in %s\n", + // name, spec, &byteArrayBody(t, className(t, c), 0)); + + // abort(t); + } +} + +Finder* +getFinder(Thread* t, const char* name, unsigned nameLength) +{ + ACQUIRE(t, t->m->referenceLock); + + for (object p = root(t, Machine::VirtualFileFinders); + p; p = finderNext(t, p)) + { + if (byteArrayLength(t, finderName(t, p)) == nameLength + and strncmp(reinterpret_cast + (&byteArrayBody(t, finderName(t, p), 0)), + name, nameLength)) + { + return static_cast(finderFinder(t, p)); + } + } + + object n = makeByteArray(t, nameLength + 1); + memcpy(&byteArrayBody(t, n, 0), name, nameLength); + + void* p = t->m->libraries->resolve + (reinterpret_cast(&byteArrayBody(t, n, 0))); + + if (p) { + uint8_t* (*function)(unsigned*); + memcpy(&function, &p, BytesPerWord); + + unsigned size; + uint8_t* data = function(&size); + if (data) { + Finder* f = makeFinder(t->m->system, t->m->heap, data, size); + object finder = makeFinder + (t, f, n, root(t, Machine::VirtualFileFinders)); + + setRoot(t, Machine::VirtualFileFinders, finder); + + return f; + } + } + + return 0; +} + +} // namespace vm + +#endif//CLASSPATH_COMMON_H diff --git a/src/common.h b/src/avian/common.h similarity index 79% rename from src/common.h rename to src/avian/common.h index adab2f587f..f3618a8e83 100644 --- a/src/common.h +++ b/src/avian/common.h @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#ifndef COMMON_H -#define COMMON_H +#ifndef AVIAN_COMMON_H +#define AVIAN_COMMON_H #ifndef __STDC_CONSTANT_MACROS # define __STDC_CONSTANT_MACROS @@ -20,25 +20,25 @@ #include "stddef.h" #include "string.h" #include "stdio.h" -#include "types.h" +#include "avian/types.h" #include "math.h" #ifdef _MSC_VER #include "float.h" +#include + +#ifdef powerpc +# undef powerpc +#endif + +#ifdef linux +# undef linux +#endif // don't complain about using 'this' in member initializers: # pragma warning(disable:4355) -typedef char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef int int32_t; -typedef unsigned int uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; - #define strncasecmp _strnicmp #define FP_NAN 0 @@ -58,11 +58,6 @@ inline int fpclassify(double d) { return FP_UNDEF; } -#define INT32_MIN ((int32_t) _I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t) _I64_MIN) -#define INT64_MAX _I64_MAX - inline int signbit(double d) { return _copysign(1.0, d) < 0; } @@ -86,15 +81,18 @@ inline int signbit(double d) { # ifdef _M_IX86 typedef int32_t intptr_t; typedef uint32_t uintptr_t; -# define UINT64_C(x) x##LL # define ARCH_x86_32 # define BYTES_PER_WORD 4 # elif defined _M_X64 typedef int64_t intptr_t; typedef uint64_t uintptr_t; -# define UINT64_C(x) x##L # define ARCH_x86_64 -@ define BYTES_PER_WORD 8 +# define BYTES_PER_WORD 8 +# elif defined _M_ARM_FP +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; +# define ARCH_arm +# define BYTES_PER_WORD 4 # else # error "unsupported architecture" # endif @@ -107,7 +105,7 @@ typedef intptr_t intptr_alias_t; #else // not _MSC_VER -# include "stdint.h" +# include # define BYTES_PER_WORD __SIZEOF_POINTER__ @@ -214,6 +212,9 @@ typedef intptr_t __attribute__((__may_alias__)) intptr_alias_t; type name; \ } MAKE_NAME(resource_)(name); +#ifdef _MSC_VER +# pragma warning( disable : 4291 ) +#endif inline void* operator new(size_t, void* p) throw() { return p; } namespace vm { @@ -226,23 +227,6 @@ alias(void* p, unsigned offset) #ifdef _MSC_VER -template -class RuntimeArray { - public: - RuntimeArray(unsigned size): - body(static_cast(malloc(size * sizeof(T)))) - { } - - ~RuntimeArray() { - free(body); - } - - T* body; -}; - -# define RUNTIME_ARRAY(type, name, size) RuntimeArray name(size); -# define RUNTIME_ARRAY_BODY(name) name.body - inline int vsnprintf(char* dst, size_t size, const char* format, va_list a) { @@ -272,9 +256,6 @@ fopen(const char* name, const char* mode) #else // not _MSC_VER -# define RUNTIME_ARRAY(type, name, size) type name[size]; -# define RUNTIME_ARRAY_BODY(name) name - inline int vsnprintf(char* dst, size_t size, const char* format, va_list a) { @@ -307,24 +288,6 @@ const uintptr_t PointerMask const unsigned LikelyPageSizeInBytes = 4 * 1024; -inline unsigned -max(unsigned a, unsigned b) -{ - return (a > b ? a : b); -} - -inline unsigned -min(unsigned a, unsigned b) -{ - return (a < b ? a : b); -} - -inline unsigned -avg(unsigned a, unsigned b) -{ - return (a + b) / 2; -} - inline unsigned pad(unsigned n, unsigned alignment) { @@ -349,35 +312,17 @@ padWord(uintptr_t n) return padWord(n, BytesPerWord); } -inline unsigned -ceiling(unsigned n, unsigned d) -{ - return (n + d - 1) / d; +inline bool fitsInInt8(int64_t v) { + return v == static_cast(v); } -inline bool -powerOfTwo(unsigned n) -{ - for (; n > 2; n >>= 1) if (n & 1) return false; - return true; +inline bool fitsInInt16(int64_t v) { + return v == static_cast(v); } -inline unsigned -nextPowerOfTwo(unsigned n) -{ - unsigned r = 1; - while (r < n) r <<= 1; - return r; +inline bool fitsInInt32(int64_t v) { + return v == static_cast(v); } - -inline unsigned -log(unsigned n) -{ - unsigned r = 0; - for (unsigned i = 1; i < n; ++r) i <<= 1; - return r; -} - template inline unsigned wordOf(unsigned i) @@ -476,14 +421,14 @@ getBits(T* map, unsigned bitsPerRecord, unsigned index) template inline T& -cast(void* p, unsigned offset) +fieldAtOffset(void* p, unsigned offset) { return *reinterpret_cast(static_cast(p) + offset); } template inline T* -mask(T* p) +maskAlignedPointer(T* p) { return reinterpret_cast(reinterpret_cast(p) & PointerMask); } @@ -524,6 +469,12 @@ hash(const uint16_t* s, unsigned length) return h; } +inline void +write4(uint8_t* dst, uint32_t v) +{ + memcpy(dst, &v, 4); +} + inline uint32_t floatToBits(float f) { @@ -593,57 +544,6 @@ equal(const void* a, unsigned al, const void* b, unsigned bl) } } -class Machine; -class Thread; - -struct Object { }; - -typedef Object* object; - -typedef uint8_t jboolean; -typedef int8_t jbyte; -typedef uint16_t jchar; -typedef int16_t jshort; -typedef int32_t jint; -typedef int64_t jlong; -typedef float jfloat; -typedef double jdouble; - -typedef jint jsize; - -typedef object* jobject; - -typedef jobject jclass; -typedef jobject jthrowable; -typedef jobject jstring; -typedef jobject jweak; - -typedef jobject jarray; -typedef jarray jbooleanArray; -typedef jarray jbyteArray; -typedef jarray jcharArray; -typedef jarray jshortArray; -typedef jarray jintArray; -typedef jarray jlongArray; -typedef jarray jfloatArray; -typedef jarray jdoubleArray; -typedef jarray jobjectArray; - -typedef uintptr_t jfieldID; -typedef uintptr_t jmethodID; - -union jvalue { - jboolean z; - jbyte b; - jchar c; - jshort s; - jint i; - jlong j; - jfloat f; - jdouble d; - jobject l; -}; - } // namespace vm -#endif//COMMON_H +#endif // AVIAN_COMMON_H diff --git a/src/constants.h b/src/avian/constants.h similarity index 100% rename from src/constants.h rename to src/avian/constants.h diff --git a/src/jnienv.h b/src/avian/embed.h similarity index 58% rename from src/jnienv.h rename to src/avian/embed.h index 5b8c46685b..2f5b5f14ef 100644 --- a/src/jnienv.h +++ b/src/avian/embed.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008, Avian Contributors +/* Copyright (c) 2008-2012, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -8,16 +8,10 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#ifndef JNIENV_H -#define JNIENV_H +#ifndef EMBED_H +#define EMBED_H -#include "machine.h" +#define RESID_MAIN_CLASS 100 +#define RESID_BOOT_JAR L"BOOT.JAR" -namespace vm { - -void -populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable); - -} // namespace vm - -#endif//JNIENV_H +#endif diff --git a/src/environment.h b/src/avian/environment.h similarity index 100% rename from src/environment.h rename to src/avian/environment.h diff --git a/src/finder.h b/src/avian/finder.h similarity index 98% rename from src/finder.h rename to src/avian/finder.h index f4582417fa..935734537e 100644 --- a/src/finder.h +++ b/src/avian/finder.h @@ -11,9 +11,9 @@ #ifndef FINDER_H #define FINDER_H -#include "common.h" -#include "system.h" -#include "allocator.h" +#include "avian/common.h" +#include +#include "avian/allocator.h" namespace vm { diff --git a/src/heapwalk.h b/src/avian/heapwalk.h similarity index 95% rename from src/heapwalk.h rename to src/avian/heapwalk.h index 25c681d6a3..eac896fd5e 100644 --- a/src/heapwalk.h +++ b/src/avian/heapwalk.h @@ -11,7 +11,8 @@ #ifndef HEAPWALK_H #define HEAPWALK_H -#include "common.h" +#include "avian/common.h" +#include "java-common.h" namespace vm { diff --git a/src/avian/java-common.h b/src/avian/java-common.h new file mode 100644 index 0000000000..ba1be9269a --- /dev/null +++ b/src/avian/java-common.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef JAVA_COMMON_H +#define JAVA_COMMON_H + +namespace vm { + +class Machine; +class Thread; + +struct Object { }; + +typedef Object* object; + +typedef uint8_t jboolean; +typedef int8_t jbyte; +typedef uint16_t jchar; +typedef int16_t jshort; +typedef int32_t jint; +typedef int64_t jlong; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +typedef object* jobject; + +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jweak; + +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +typedef uintptr_t jfieldID; +typedef uintptr_t jmethodID; + +union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +}; + +} // namespace vm + +#endif // JAVA_COMMON_H diff --git a/src/avian/jnienv.h b/src/avian/jnienv.h new file mode 100644 index 0000000000..a1f2ff90aa --- /dev/null +++ b/src/avian/jnienv.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2008, 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. */ + +#ifndef JNIENV_H +#define JNIENV_H + +#include "avian/machine.h" + +#define BOOTSTRAP_PROPERTY "avian.bootstrap" +#define JAVA_COMMAND_PROPERTY "sun.java.command" +#define JAVA_LAUNCHER_PROPERTY "sun.java.launcher" +#define CRASHDIR_PROPERTY "avian.crash.dir" +#define EMBED_PREFIX_PROPERTY "avian.embed.prefix" +#define CLASSPATH_PROPERTY "java.class.path" +#define JAVA_HOME_PROPERTY "java.home" +#define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p" +#define BOOTCLASSPATH_OPTION "bootclasspath" +#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" +#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" + +namespace vm { + +void +populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable); + +} // namespace vm + +#endif//JNIENV_H diff --git a/src/lzma-util.h b/src/avian/lzma-util.h similarity index 93% rename from src/lzma-util.h rename to src/avian/lzma-util.h index e7fc3093a0..cfe4103245 100644 --- a/src/lzma-util.h +++ b/src/avian/lzma-util.h @@ -11,10 +11,10 @@ #ifndef LZMA_UTIL_H #define LZMA_UTIL_H -#include "lzma.h" +#include "avian/lzma.h" #include "C/Types.h" -#include "system.h" -#include "allocator.h" +#include +#include "avian/allocator.h" namespace vm { diff --git a/src/lzma.h b/src/avian/lzma.h similarity index 90% rename from src/lzma.h rename to src/avian/lzma.h index 5e6ba35a82..bd30671ed9 100644 --- a/src/lzma.h +++ b/src/avian/lzma.h @@ -11,8 +11,8 @@ #ifndef LZMA_H #define LZMA_H -#include "system.h" -#include "allocator.h" +#include +#include "avian/allocator.h" namespace vm { diff --git a/src/machine.h b/src/avian/machine.h similarity index 95% rename from src/machine.h rename to src/avian/machine.h index a9cba4024e..ae6a0368da 100644 --- a/src/machine.h +++ b/src/avian/machine.h @@ -11,13 +11,16 @@ #ifndef MACHINE_H #define MACHINE_H -#include "common.h" -#include "system.h" -#include "heap.h" -#include "finder.h" -#include "processor.h" -#include "constants.h" -#include "arch.h" +#include "avian/common.h" +#include "java-common.h" +#include +#include +#include "avian/finder.h" +#include "avian/processor.h" +#include "avian/constants.h" +#include "avian/arch.h" + +using namespace avian::util; #ifdef PLATFORM_WINDOWS # define JNICALL __stdcall @@ -101,6 +104,8 @@ const bool DebugStack = false; const bool DebugMonitors = false; const bool DebugReferences = false; +const bool AbortOnOutOfMemoryError = false; + const uintptr_t HashTakenMark = 1; const uintptr_t ExtendedMark = 2; const uintptr_t FixedMark = 3; @@ -1206,6 +1211,7 @@ class Machine { BootLoader, AppLoader, BootstrapClassMap, + PackageMap, FindLoadedClassMethod, LoadClassMethod, MonitorMap, @@ -1226,10 +1232,12 @@ class Machine { OutOfMemoryError, Shutdown, VirtualFileFinders, - VirtualFiles + VirtualFiles, + ArrayInterfaceTable, + ThreadTerminated }; - static const unsigned RootCount = VirtualFiles + 1; + static const unsigned RootCount = ThreadTerminated + 1; Machine(System* system, Heap* heap, Finder* bootFinder, Finder* appFinder, Processor* processor, Classpath* classpath, const char** properties, @@ -1535,6 +1543,18 @@ class Classpath { virtual object makeThread(Thread* t, Thread* parent) = 0; + virtual object + makeJMethod(Thread* t, object vmMethod) = 0; + + virtual object + getVMMethod(Thread* t, object jmethod) = 0; + + virtual object + makeJField(Thread* t, object vmField) = 0; + + virtual object + getVMField(Thread* t, object jfield) = 0; + virtual void clearInterrupted(Thread* t) = 0; @@ -1544,12 +1564,37 @@ class Classpath { virtual void resolveNative(Thread* t, object method) = 0; + virtual void + interceptMethods(Thread* t) = 0; + + virtual void + preBoot(Thread* t) = 0; + virtual void boot(Thread* t) = 0; virtual const char* bootClasspath() = 0; + virtual void + updatePackageMap(Thread* t, object class_) = 0; + + virtual object + makeDirectByteBuffer(Thread* t, void* p, jlong capacity) = 0; + + virtual void* + getDirectBufferAddress(Thread* t, object buffer) = 0; + + virtual int64_t + getDirectBufferCapacity(Thread* t, object buffer) = 0; + + virtual bool + canTailCall(Thread* t, object caller, object calleeClassName, + object calleeMethodName, object calleeMethodSpec) = 0; + + virtual void + shutDown(Thread* t) = 0; + virtual void dispose() = 0; }; @@ -1582,7 +1627,8 @@ class ThreadRuntimeArray: public Thread::Resource { #else // not _MSC_VER -# define THREAD_RUNTIME_ARRAY(thread, type, name, size) type name[size]; +# define THREAD_RUNTIME_ARRAY(thread, type, name, size) \ + type name##_body[size]; #endif // not _MSC_VER @@ -1595,7 +1641,7 @@ typedef uint64_t (JNICALL *FastNativeFunction)(Thread*, object, uintptr_t*); inline object objectClass(Thread*, object o) { - return mask(cast(o, 0)); + return maskAlignedPointer(fieldAtOffset(o, 0)); } inline unsigned @@ -1656,7 +1702,7 @@ release(Thread* t, Reference* r) } void -collect(Thread* t, Heap::CollectionType type); +collect(Thread* t, Heap::CollectionType type, int pendingAllocation = 0); void shutDown(Thread* t); @@ -1742,24 +1788,8 @@ class RawMonitorResource: public Thread::Resource { System::Monitor* m; }; -inline void NO_RETURN -abort(Thread* t) -{ - abort(t->m->system); -} - -#ifndef NDEBUG -inline void -assert(Thread* t, bool v) -{ - assert(t->m->system, v); -} -#endif // not NDEBUG - -inline void -expect(Thread* t, bool v) -{ - expect(t->m->system, v); +inline Aborter* getAborter(Thread* t) { + return t->m->system; } class FixedAllocator: public Allocator { @@ -1768,8 +1798,8 @@ class FixedAllocator: public Allocator { s(s), base(base), offset(0), capacity(capacity) { } - virtual void* tryAllocate(unsigned) { - abort(s); + virtual void* tryAllocate(unsigned size) { + return allocate(size); } void* allocate(unsigned size, unsigned padAlignment) { @@ -1802,7 +1832,7 @@ class FixedAllocator: public Allocator { inline bool ensure(Thread* t, unsigned sizeInBytes) { - if (t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords) { if (sizeInBytes <= ThreadBackupHeapSizeInBytes) { @@ -1829,11 +1859,11 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, inline object allocateSmall(Thread* t, unsigned sizeInBytes) { - assert(t, t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + assert(t, t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) <= ThreadHeapSizeInWords); object o = reinterpret_cast(t->heap + t->heapIndex); - t->heapIndex += ceiling(sizeInBytes, BytesPerWord); + t->heapIndex += ceilingDivide(sizeInBytes, BytesPerWord); return o; } @@ -1842,7 +1872,7 @@ allocate(Thread* t, unsigned sizeInBytes, bool objectMask) { stress(t); - if (UNLIKELY(t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + if (UNLIKELY(t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords or t->m->exclusive)) { @@ -1867,18 +1897,18 @@ mark(Thread* t, object o, unsigned offset) inline void set(Thread* t, object target, unsigned offset, object value) { - cast(target, offset) = value; + fieldAtOffset(target, offset) = value; mark(t, target, offset); } inline void setObjectClass(Thread*, object o, object value) { - cast(o, 0) + fieldAtOffset(o, 0) = reinterpret_cast (reinterpret_cast(value) | (reinterpret_cast - (cast(o, 0)) & (~PointerMask))); + (fieldAtOffset(o, 0)) & (~PointerMask))); } inline const char* @@ -1965,6 +1995,7 @@ addThread(Thread* t, Thread* p) ACQUIRE_RAW(t, t->m->stateLock); assert(t, p->state == Thread::NoState); + expect(t, t->state == Thread::ActiveState || t->state == Thread::ExclusiveState); p->state = Thread::IdleState; ++ t->m->threadCount; @@ -2133,9 +2164,9 @@ baseSize(Thread* t, object o, object class_) { assert(t, classFixedSize(t, class_) >= BytesPerWord); - return ceiling(classFixedSize(t, class_), BytesPerWord) - + ceiling(classArrayElementSize(t, class_) - * cast(o, classFixedSize(t, class_) - BytesPerWord), + return ceilingDivide(classFixedSize(t, class_), BytesPerWord) + + ceilingDivide(classArrayElementSize(t, class_) + * fieldAtOffset(o, classFixedSize(t, class_) - BytesPerWord), BytesPerWord); } @@ -2274,7 +2305,7 @@ inline uintptr_t& extendedWord(Thread* t UNUSED, object o, unsigned baseSize) { assert(t, objectExtended(t, o)); - return cast(o, baseSize * BytesPerWord); + return fieldAtOffset(o, baseSize * BytesPerWord); } inline unsigned @@ -2509,6 +2540,9 @@ emptyMethod(Thread* t, object method) object parseUtf8(Thread* t, const char* data, unsigned length); +object +parseUtf8(Thread* t, object array); + object parseClass(Thread* t, object loader, const uint8_t* data, unsigned length, Machine::Type throwType = Machine::NoClassDefFoundErrorType); @@ -2663,6 +2697,19 @@ makeThrowable(Thread* t, Machine::Type type, const char* format, ...) void popResources(Thread* t); +} // namespace vm + +JNIEXPORT void +vmPrintTrace(vm::Thread* t); + +JNIEXPORT void +vmfPrintTrace(vm::Thread* t, FILE* out); + +namespace vm { + +void +dumpHeap(Thread* t, FILE* out); + inline void NO_RETURN throw_(Thread* t, object e) { @@ -2673,6 +2720,28 @@ throw_(Thread* t, object e) t->exception = e; + if (objectClass(t, e) == type(t, Machine::OutOfMemoryErrorType)) { +#ifdef AVIAN_HEAPDUMP + if (not t->m->dumpedHeapOnOOM) { + t->m->dumpedHeapOnOOM = true; + const char* path = findProperty(t, "avian.heap.dump"); + if (path) { + FILE* out = vm::fopen(path, "wb"); + if (out) { + dumpHeap(t, out); + fclose(out); + } + } + } +#endif//AVIAN_HEAPDUMP + + if (AbortOnOutOfMemoryError) { + fprintf(stderr, "OutOfMemoryError\n"); + vmPrintTrace(t); + abort(); + } + } + // printTrace(t, e); popResources(t); @@ -2773,7 +2842,7 @@ objectArrayLength(Thread* t UNUSED, object array) { assert(t, classFixedSize(t, objectClass(t, array)) == BytesPerWord * 2); assert(t, classArrayElementSize(t, objectClass(t, array)) == BytesPerWord); - return cast(array, BytesPerWord); + return fieldAtOffset(array, BytesPerWord); } inline object& @@ -2784,7 +2853,7 @@ objectArrayBody(Thread* t UNUSED, object array, unsigned index) assert(t, classObjectMask(t, objectClass(t, array)) == classObjectMask(t, arrayBody (t, t->m->types, Machine::ArrayType))); - return cast(array, ArrayBody + (index * BytesPerWord)); + return fieldAtOffset(array, ArrayBody + (index * BytesPerWord)); } unsigned @@ -2820,7 +2889,7 @@ inline bool atomicCompareAndSwapObject(Thread* t, object target, unsigned offset, object old, object new_) { - if (atomicCompareAndSwap(&cast(target, offset), + if (atomicCompareAndSwap(&fieldAtOffset(target, offset), reinterpret_cast(old), reinterpret_cast(new_))) { @@ -3330,7 +3399,7 @@ inline unsigned singletonMaskSize(unsigned count, unsigned bitsPerWord) { if (count) { - return ceiling(count + 2, bitsPerWord); + return ceilingDivide(count + 2, bitsPerWord); } return 0; } @@ -3346,7 +3415,7 @@ singletonMaskSize(Thread* t, object singleton) { unsigned length = singletonLength(t, singleton); if (length) { - return ceiling(length + 2, BitsPerWord + 1); + return ceilingDivide(length + 2, BitsPerWord + 1); } return 0; } @@ -3429,7 +3498,7 @@ singletonBit(Thread* t, object singleton, unsigned start, unsigned index) inline unsigned poolMaskSize(unsigned count, unsigned bitsPerWord) { - return ceiling(count, bitsPerWord); + return ceilingDivide(count, bitsPerWord); } inline unsigned @@ -3441,7 +3510,7 @@ poolMaskSize(unsigned count) inline unsigned poolMaskSize(Thread* t, object pool) { - return ceiling(singletonCount(t, pool), BitsPerWord + 1); + return ceilingDivide(singletonCount(t, pool), BitsPerWord + 1); } inline unsigned @@ -3454,7 +3523,7 @@ inline object resolveClassInObject(Thread* t, object loader, object container, unsigned classOffset, bool throw_ = true) { - object o = cast(container, classOffset); + object o = fieldAtOffset(container, classOffset); loadMemoryBarrier(); @@ -3801,14 +3870,11 @@ populateMultiArray(Thread* t, object array, int32_t* counts, unsigned index, unsigned dimensions); object -getCaller(Thread* t, unsigned target); +getCaller(Thread* t, unsigned target, bool skipMethodInvoke = false); object defineClass(Thread* t, object loader, const uint8_t* buffer, unsigned length); -void -dumpHeap(Thread* t, FILE* out); - inline object methodClone(Thread* t, object method) { @@ -3893,9 +3959,6 @@ errorLog(Thread* t) } // namespace vm -JNIEXPORT void -vmPrintTrace(vm::Thread* t); - JNIEXPORT void* vmAddressFromLine(vm::Thread* t, vm::object m, unsigned line); diff --git a/src/powerpc.h b/src/avian/powerpc.h similarity index 99% rename from src/powerpc.h rename to src/avian/powerpc.h index 6be4d34286..fd1c9eaee5 100644 --- a/src/powerpc.h +++ b/src/avian/powerpc.h @@ -11,8 +11,8 @@ #ifndef POWERPC_H #define POWERPC_H -#include "types.h" -#include "common.h" +#include "avian/types.h" +#include "avian/common.h" #ifdef __APPLE__ # include "mach/mach_types.h" diff --git a/src/process.h b/src/avian/process.h similarity index 92% rename from src/process.h rename to src/avian/process.h index 04fcf57c7d..42fab2b2c9 100644 --- a/src/process.h +++ b/src/avian/process.h @@ -11,10 +11,10 @@ #ifndef PROCESS_H #define PROCESS_H -#include "common.h" -#include "system.h" -#include "machine.h" -#include "constants.h" +#include "avian/common.h" +#include +#include "avian/machine.h" +#include "avian/constants.h" namespace vm { diff --git a/src/processor.h b/src/avian/processor.h similarity index 94% rename from src/processor.h rename to src/avian/processor.h index 6da91dddce..d6e3128433 100644 --- a/src/processor.h +++ b/src/avian/processor.h @@ -11,13 +11,18 @@ #ifndef PROCESSOR_H #define PROCESSOR_H -#include "common.h" -#include "system.h" -#include "heap.h" +#include "avian/common.h" +#include +#include #include "bootimage.h" -#include "heapwalk.h" -#include "zone.h" -#include "assembler.h" +#include "avian/heapwalk.h" +#include "avian/zone.h" + +namespace avian { +namespace codegen { +class DelayedPromise; +} +} namespace vm { @@ -142,7 +147,7 @@ class Processor { virtual void compileMethod(Thread* t, Zone* zone, object* constants, object* calls, - DelayedPromise** addresses, object method, + avian::codegen::DelayedPromise** addresses, object method, OffsetResolver* resolver) = 0; virtual void diff --git a/src/target-fields.h b/src/avian/target-fields.h similarity index 100% rename from src/target-fields.h rename to src/avian/target-fields.h diff --git a/src/target.h b/src/avian/target.h similarity index 92% rename from src/target.h rename to src/avian/target.h index 996d76348f..8f50e39dcd 100644 --- a/src/target.h +++ b/src/avian/target.h @@ -11,7 +11,8 @@ #ifndef TARGET_H #define TARGET_H -#include "target-fields.h" +#include "avian/target-fields.h" +#include "avian/common.h" namespace vm { @@ -22,11 +23,9 @@ targetV1(T v) return v; } -#ifdef TARGET_OPPOSITE_ENDIAN - template inline T -targetV2(T v) +swapV2(T v) { return (((v >> 8) & 0xFF) | ((v << 8))); @@ -34,7 +33,7 @@ targetV2(T v) template inline T -targetV4(T v) +swapV4(T v) { return (((v >> 24) & 0x000000FF) | ((v >> 8) & 0x0000FF00) | @@ -44,7 +43,7 @@ targetV4(T v) template inline T -targetV8(T v) +swapV8(T v) { return (((static_cast(v) >> 56) & UINT64_C(0x00000000000000FF)) | ((static_cast(v) >> 40) & UINT64_C(0x000000000000FF00)) | @@ -56,6 +55,29 @@ targetV8(T v) ((static_cast(v) << 56))); } +#ifdef TARGET_OPPOSITE_ENDIAN + +template +inline T +targetV2(T v) +{ + return swapV2(v); +} + +template +inline T +targetV4(T v) +{ + return swapV4(v); +} + +template +inline T +targetV8(T v) +{ + return swapV8(v); +} + #else template inline T diff --git a/src/types.h b/src/avian/types.h similarity index 100% rename from src/types.h rename to src/avian/types.h diff --git a/src/util.h b/src/avian/util.h similarity index 98% rename from src/util.h rename to src/avian/util.h index 40a70b5bfb..1dceaf108a 100644 --- a/src/util.h +++ b/src/avian/util.h @@ -11,8 +11,8 @@ #ifndef UTIL_H #define UTIL_H -#include "machine.h" -#include "zone.h" +#include "avian/machine.h" +#include "avian/zone.h" namespace vm { diff --git a/src/x86.h b/src/avian/x86.h similarity index 99% rename from src/x86.h rename to src/avian/x86.h index bda55bee7d..be043e70de 100644 --- a/src/x86.h +++ b/src/avian/x86.h @@ -11,8 +11,8 @@ #ifndef X86_H #define X86_H -#include "types.h" -#include "common.h" +#include "avian/types.h" +#include "avian/common.h" #ifdef _MSC_VER # include "windows.h" diff --git a/src/zlib-custom.h b/src/avian/zlib-custom.h similarity index 100% rename from src/zlib-custom.h rename to src/avian/zlib-custom.h diff --git a/src/zone.h b/src/avian/zone.h similarity index 68% rename from src/zone.h rename to src/avian/zone.h index 9e35cdcad7..56da22faff 100644 --- a/src/zone.h +++ b/src/avian/zone.h @@ -11,8 +11,10 @@ #ifndef ZONE_H #define ZONE_H -#include "system.h" -#include "allocator.h" +#include +#include "avian/allocator.h" + +#include namespace vm { @@ -20,10 +22,13 @@ class Zone: public Allocator { public: class Segment { public: - Segment(Segment* next, unsigned size): next(next), size(size) { } + Segment(Segment* next, unsigned size): + next(next), size(size), position(0) + { } Segment* next; uintptr_t size; + uintptr_t position; uint8_t data[0]; }; @@ -31,7 +36,6 @@ class Zone: public Allocator { s(s), allocator(allocator), segment(0), - position(0), minimumFootprint(minimumFootprint < sizeof(Segment) ? 0 : minimumFootprint - sizeof(Segment)) { } @@ -55,10 +59,10 @@ class Zone: public Allocator { } bool tryEnsure(unsigned space) { - if (segment == 0 or position + space > segment->size) { + if (segment == 0 or segment->position + space > segment->size) { unsigned size = padToPage - (max - (space, max + (avian::util::max + (space, avian::util::max (minimumFootprint, segment == 0 ? 0 : segment->size * 2)) + sizeof(Segment)); @@ -72,26 +76,24 @@ class Zone: public Allocator { } segment = new (p) Segment(segment, size - sizeof(Segment)); - position = 0; } return true; } void ensure(unsigned space) { - if (segment == 0 or position + space > segment->size) { + if (segment == 0 or segment->position + space > segment->size) { unsigned size = padToPage(space + sizeof(Segment)); segment = new (allocator->allocate(size)) Segment(segment, size - sizeof(Segment)); - position = 0; } } virtual void* tryAllocate(unsigned size) { size = pad(size); if (tryEnsure(size)) { - void* r = segment->data + position; - position += size; + void* r = segment->data + segment->position; + segment->position += size; return r; } else { return 0; @@ -99,17 +101,41 @@ class Zone: public Allocator { } virtual void* allocate(unsigned size) { + size = pad(size); void* p = tryAllocate(size); if (p) { return p; } else { ensure(size); - void* r = segment->data + position; - position += size; + void* r = segment->data + segment->position; + segment->position += size; return r; } } + void* peek(unsigned size) { + size = pad(size); + Segment* s = segment; + while (s->position < size) { + size -= s->position; + s = s->next; + } + return s->data + (s->position - size); + } + + void pop(unsigned size) { + size = pad(size); + Segment* s = segment; + while (s->position < size) { + size -= s->position; + Segment* next = s->next; + allocator->free(s, sizeof(Segment) + s->size); + s = next; + } + s->position -= size; + segment = s; + } + virtual void free(const void*, unsigned) { // not supported abort(s); @@ -119,7 +145,6 @@ class Zone: public Allocator { Allocator* allocator; void* context; Segment* segment; - unsigned position; unsigned minimumFootprint; }; diff --git a/src/bootimage-template.cpp b/src/bootimage-template.cpp index d96a586fcd..bf32267ece 100644 --- a/src/bootimage-template.cpp +++ b/src/bootimage-template.cpp @@ -1,7 +1,7 @@ const unsigned NAME(BootMask) = (~static_cast(0)) / NAME(BytesPerWord); -const unsigned NAME(BootShift) = 32 - log(NAME(BytesPerWord)); +const unsigned NAME(BootShift) = 32 - avian::util::log(NAME(BytesPerWord)); const unsigned NAME(BootFlatConstant) = 1 << NAME(BootShift); const unsigned NAME(BootHeapOffset) = 1 << (NAME(BootShift) + 1); @@ -9,13 +9,13 @@ const unsigned NAME(BootHeapOffset) = 1 << (NAME(BootShift) + 1); inline unsigned LABEL(codeMapSize)(unsigned codeSize) { - return ceiling(codeSize, TargetBitsPerWord) * TargetBytesPerWord; + return avian::util::ceilingDivide(codeSize, TargetBitsPerWord) * TargetBytesPerWord; } inline unsigned LABEL(heapMapSize)(unsigned heapSize) { - return ceiling(heapSize, TargetBitsPerWord * TargetBytesPerWord) + return avian::util::ceilingDivide(heapSize, TargetBitsPerWord * TargetBytesPerWord) * TargetBytesPerWord; } diff --git a/src/builtin.cpp b/src/builtin.cpp index c438aebea0..b8fa0f75f1 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -8,10 +8,12 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "constants.h" -#include "processor.h" -#include "util.h" +#include "avian/machine.h" +#include "avian/constants.h" +#include "avian/processor.h" +#include "avian/util.h" + +#include using namespace vm; @@ -48,6 +50,15 @@ resolveSystemClassThrow(Thread* t, object loader, object spec) } // namespace +extern "C" JNIEXPORT void JNICALL +Avian_avian_Classes_initialize +(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + + initClass(t, this_); +} + extern "C" JNIEXPORT void JNICALL Avian_avian_Classes_acquireClassLock (Thread* t, object, uintptr_t*) @@ -73,6 +84,26 @@ Avian_avian_Classes_resolveVMClass (resolveClass(t, loader, spec, true, Machine::ClassNotFoundExceptionType)); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_defineVMClass +(Thread* t, object, uintptr_t* arguments) +{ + object loader = reinterpret_cast(arguments[0]); + object b = reinterpret_cast(arguments[1]); + int offset = arguments[2]; + int length = arguments[3]; + + uint8_t* buffer = static_cast + (t->m->heap->allocate(length)); + + THREAD_RESOURCE2(t, uint8_t*, buffer, int, length, + t->m->heap->free(buffer, length)); + + memcpy(buffer, &byteArrayBody(t, b, offset), length); + + return reinterpret_cast(defineClass(t, loader, buffer, length)); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findLoadedVMClass (Thread* t, object, uintptr_t* arguments) @@ -83,6 +114,14 @@ Avian_avian_SystemClassLoader_findLoadedVMClass return search(t, loader, name, findLoadedClass, true); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_vmClass +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (jclassVmClass(t, reinterpret_cast(arguments[0]))); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_SystemClassLoader_findVMClass (Thread* t, object, uintptr_t* arguments) @@ -139,7 +178,8 @@ Avian_avian_Machine_dumpHeap } fclose(out); } else { - throwNew(t, Machine::RuntimeExceptionType, "file not found: %s", n); + throwNew(t, Machine::RuntimeExceptionType, "file not found: %s", + RUNTIME_ARRAY_BODY(n)); } } @@ -155,7 +195,7 @@ Avian_java_lang_Runtime_exit } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_getContentLength (Thread* t, object, uintptr_t* arguments) { object path = reinterpret_cast(*arguments); @@ -179,7 +219,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_open +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_open (Thread* t, object, uintptr_t* arguments) { object path = reinterpret_cast(*arguments); @@ -200,7 +240,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_available +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_available (Thread*, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -211,7 +251,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_available } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_read__JI +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_read__JI (Thread*, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -226,7 +266,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_read__JI } extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_read__JI_3BII +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_read__JI_3BII (Thread* t, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -251,7 +291,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_read__JI_3BII } extern "C" JNIEXPORT void JNICALL -Avian_avian_resource_Handler_00024ResourceInputStream_close +Avian_avian_avianvmresource_Handler_00024ResourceInputStream_close (Thread*, object, uintptr_t* arguments) { int64_t peer; memcpy(&peer, arguments, 8); @@ -365,7 +405,7 @@ Avian_sun_misc_Unsafe_setMemory ACQUIRE(t, t->m->referenceLock); if (base) { - memset(&cast(base, offset), value, count); + memset(&fieldAtOffset(base, offset), value, count); } else { memset(reinterpret_cast(offset), value, count); } @@ -528,11 +568,11 @@ Avian_sun_misc_Unsafe_copyMemory ACQUIRE(t, t->m->referenceLock); void* src = srcBase - ? &cast(srcBase, srcOffset) + ? &fieldAtOffset(srcBase, srcOffset) : reinterpret_cast(srcOffset); void* dst = dstBase - ? &cast(dstBase, dstOffset) + ? &fieldAtOffset(dstBase, dstOffset) : reinterpret_cast(dstOffset); memcpy(dst, src, count); @@ -563,3 +603,23 @@ Avian_java_nio_FixedArrayByteBuffer_allocateFixed return reinterpret_cast(array); } + +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_compareAndSwapInt +(Thread*, object, uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[1]); + int64_t offset; memcpy(&offset, arguments + 2, 8); + uint32_t expect = arguments[4]; + uint32_t update = arguments[5]; + + return atomicCompareAndSwap32 + (&fieldAtOffset(target, offset), expect, update); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_primitiveClass +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(primitiveClass(t, arguments[0])); +} diff --git a/src/classpath-android.cpp b/src/classpath-android.cpp new file mode 100644 index 0000000000..87fa114cf0 --- /dev/null +++ b/src/classpath-android.cpp @@ -0,0 +1,1887 @@ +/* Copyright (c) 2010-2012, 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. */ + +struct JavaVM; + +extern "C" int JNI_OnLoad(JavaVM*, void*); + +#define _POSIX_C_SOURCE 200112L +#undef _GNU_SOURCE +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/process.h" + +using namespace vm; + +namespace { + +namespace local { + +void* +getDirectBufferAddress(Thread* t, object b) +{ + PROTECT(t, b); + + object field = resolveField + (t, objectClass(t, b), "effectiveDirectAddress", "J"); + + return reinterpret_cast + (fieldAtOffset(b, fieldOffset(t, field))); +} + +void JNICALL +loadLibrary(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[1]); + + unsigned length = stringLength(t, name); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), true, true); +} + +int64_t JNICALL +appLoader(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(root(t, Machine::AppLoader)); +} + +int64_t JNICALL +mapData(Thread*, object, uintptr_t*); + +void JNICALL +closeMemoryMappedFile(Thread*, object, uintptr_t*); + +object +makeMethodOrConstructor(Thread* t, object c, unsigned index) +{ + PROTECT(t, c); + + object method = arrayBody + (t, classMethodTable(t, jclassVmClass(t, c)), index); + PROTECT(t, method); + + unsigned parameterCount; + unsigned returnTypeSpec; + object parameterTypes = resolveParameterJTypes + (t, classLoader(t, methodClass(t, method)), methodSpec(t, method), + ¶meterCount, &returnTypeSpec); + PROTECT(t, parameterTypes); + + object returnType = resolveJType + (t, classLoader(t, methodClass(t, method)), reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), returnTypeSpec)), + byteArrayLength(t, methodSpec(t, method)) - 1 - returnTypeSpec); + PROTECT(t, returnType); + + object exceptionTypes = resolveExceptionJTypes + (t, classLoader(t, methodClass(t, method)), methodAddendum(t, method)); + + if (byteArrayBody(t, methodName(t, method), 0) == '<') { + return makeJconstructor + (t, 0, c, parameterTypes, exceptionTypes, 0, 0, 0, 0, index); + } else { + PROTECT(t, exceptionTypes); + + object name = t->m->classpath->makeString + (t, methodName(t, method), 0, + byteArrayLength(t, methodName(t, method)) - 1); + + return makeJmethod + (t, 0, index, c, name, parameterTypes, exceptionTypes, returnType, 0, 0, + 0, 0, 0); + } +} + +object +makeField(Thread* t, object c, unsigned index) +{ + PROTECT(t, c); + + object field = arrayBody + (t, classFieldTable(t, jclassVmClass(t, c)), index); + + PROTECT(t, field); + + object type = resolveClassBySpec + (t, classLoader(t, fieldClass(t, field)), + reinterpret_cast + (&byteArrayBody(t, fieldSpec(t, field), 0)), + byteArrayLength(t, fieldSpec(t, field)) - 1); + PROTECT(t, type); + + object name = t->m->classpath->makeString + (t, fieldName(t, field), 0, + byteArrayLength(t, fieldName(t, field)) - 1); + + return makeJfield(t, 0, c, type, 0, 0, name, index); +} + +void +initVmThread(Thread* t, object thread) +{ + PROTECT(t, thread); + + object field = resolveField + (t, objectClass(t, thread), "vmThread", "Ljava/lang/VMThread;"); + + if (fieldAtOffset(thread, fieldOffset(t, field)) == 0) { + PROTECT(t, field); + + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/VMThread"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod + (t, c, "", "(Ljava/lang/Thread;)V"); + + t->m->processor->invoke(t, constructor, instance, thread); + + set(t, thread, fieldOffset(t, field), instance); + } +} + +object +translateStackTrace(Thread* t, object raw) +{ + PROTECT(t, raw); + + object array = makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), "java/lang/StackTraceElement"), + objectArrayLength(t, raw)); + PROTECT(t, array); + + for (unsigned i = 0; i < objectArrayLength(t, array); ++i) { + object e = makeStackTraceElement(t, objectArrayBody(t, raw, i)); + + set(t, array, ArrayBody + (i * BytesPerWord), e); + } + + return array; +} + +class MyClasspath : public Classpath { + public: + MyClasspath(Allocator* allocator): + allocator(allocator), + tzdata(0) + { } + + virtual object + makeJclass(Thread* t, object class_) + { + PROTECT(t, class_); + + object c = allocate(t, FixedSizeOfJclass, true); + setObjectClass(t, c, type(t, Machine::JclassType)); + set(t, c, JclassVmClass, class_); + + return c; + } + + virtual object + makeString(Thread* t, object array, int32_t offset, int32_t length) + { + if (objectClass(t, array) == type(t, Machine::ByteArrayType)) { + PROTECT(t, array); + + object charArray = makeCharArray(t, length); + for (int i = 0; i < length; ++i) { + expect(t, (byteArrayBody(t, array, offset + i) & 0x80) == 0); + + charArrayBody(t, charArray, i) = byteArrayBody(t, array, offset + i); + } + + array = charArray; + } else { + expect(t, objectClass(t, array) == type(t, Machine::CharArrayType)); + } + + return vm::makeString(t, array, offset, length, 0); + } + + virtual object + makeThread(Thread* t, Thread* parent) + { + const unsigned NormalPriority = 5; + + object group = 0; + PROTECT(t, group); + if (parent) { + group = threadGroup(t, parent->javaThread); + } else { + resolveSystemClass + (t, root(t, Machine::BootLoader), + className(t, type(t, Machine::ThreadGroupType)), false); + + group = makeNew(t, type(t, Machine::ThreadGroupType)); + + object constructor = resolveMethod + (t, type(t, Machine::ThreadGroupType), "", "()V"); + + t->m->processor->invoke(t, constructor, group); + } + + resolveSystemClass + (t, root(t, Machine::BootLoader), + className(t, type(t, Machine::ThreadType)), false); + + object thread = makeNew(t, type(t, Machine::ThreadType)); + PROTECT(t, thread); + + object constructor = resolveMethod + (t, type(t, Machine::ThreadType), "", + "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V"); + + t->m->processor->invoke + (t, constructor, thread, group, 0, NormalPriority, false); + + set(t, thread, ThreadContextClassLoader, root(t, Machine::AppLoader)); + + initVmThread(t, thread); + + return thread; + } + + virtual object + makeJMethod(Thread* t, object vmMethod) + { + object table = classMethodTable(t, methodClass(t, vmMethod)); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (vmMethod == arrayBody(t, table, i)) { + return makeMethodOrConstructor + (t, getJClass(t, methodClass(t, vmMethod)), i); + } + } + abort(t); + } + + virtual object + getVMMethod(Thread* t, object jmethod) + { + return objectClass(t, jmethod) == type(t, Machine::JmethodType) + ? arrayBody + (t, classMethodTable + (t, jclassVmClass(t, jmethodDeclaringClass(t, jmethod))), + jmethodSlot(t, jmethod)) + : arrayBody + (t, classMethodTable + (t, jclassVmClass(t, jconstructorDeclaringClass(t, jmethod))), + jconstructorSlot(t, jmethod)); + } + + virtual object + makeJField(Thread* t, object vmField) + { + object table = classFieldTable(t, fieldClass(t, vmField)); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (vmField == arrayBody(t, table, i)) { + return makeField(t, getJClass(t, fieldClass(t, vmField)), i); + } + } + abort(t); + } + + virtual object + getVMField(Thread* t, object jfield) + { + return arrayBody + (t, classFieldTable + (t, jclassVmClass(t, jfieldDeclaringClass(t, jfield))), + jfieldSlot(t, jfield)); + } + + virtual void + clearInterrupted(Thread*) + { + // ignore + } + + virtual void + runThread(Thread* t) + { + // force monitor creation so we don't get an OutOfMemory error + // later when we try to acquire it: + objectMonitor(t, t->javaThread, true); + + THREAD_RESOURCE0(t, { + vm::acquire(t, t->javaThread); + t->flags &= ~Thread::ActiveFlag; + vm::notifyAll(t, t->javaThread); + vm::release(t, t->javaThread); + }); + + initVmThread(t, t->javaThread); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "java/lang/Thread", "run", "()V"); + + t->m->processor->invoke(t, method, t->javaThread); + } + + virtual void + resolveNative(Thread* t, object method) + { + vm::resolveNative(t, method); + } + + void + interceptMethods(Thread* t, bool updateRuntimeData) + { + { object c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/Runtime", false); + + if (c) { + PROTECT(t, c); + + intercept(t, c, "loadLibrary", + "(Ljava/lang/String;Ljava/lang/ClassLoader;)V", + voidPointer(loadLibrary), updateRuntimeData); + } + } + + { object c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/ClassLoader", false); + + if (c) { + PROTECT(t, c); + + intercept(t, c, "createSystemClassLoader", "()Ljava/lang/ClassLoader;", + voidPointer(appLoader), updateRuntimeData); + } + } + + { object c = resolveClass + (t, root(t, Machine::BootLoader), "libcore/util/ZoneInfoDB", false); + + if (c) { + PROTECT(t, c); + + intercept(t, c, "mapData", "()Llibcore/io/MemoryMappedFile;", + voidPointer(mapData), updateRuntimeData); + } + } + + { object c = resolveClass + (t, root(t, Machine::BootLoader), "libcore/io/MemoryMappedFile", + false); + + if (c) { + PROTECT(t, c); + + intercept(t, c, "close", "()V", voidPointer(closeMemoryMappedFile), + updateRuntimeData); + } + } + } + + virtual void + interceptMethods(Thread* t) + { + interceptMethods(t, false); + } + + virtual void + preBoot(Thread* t) + { + // Android's System.initSystemProperties throws an NPE if + // LD_LIBRARY_PATH is not set as of this writing: +#ifdef PLATFORM_WINDOWS + _wputenv(L"LD_LIBRARY_PATH=(dummy)"); +#else + setenv("LD_LIBRARY_PATH", "", false); +#endif + + interceptMethods(t, true); + + JNI_OnLoad(reinterpret_cast< ::JavaVM*>(t->m), 0); + } + + virtual void + boot(Thread*) + { + // ignore + } + + virtual const char* + bootClasspath() + { + return AVIAN_CLASSPATH; + } + + virtual void + updatePackageMap(Thread*, object) + { + // ignore + } + + virtual object + makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke + (t, constructor, instance, reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* + getDirectBufferAddress(Thread* t, object b) + { + return local::getDirectBufferAddress(t, b); + } + + virtual int64_t + getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + object field = resolveField + (t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, fieldOffset(t, field)); + } + + virtual bool + canTailCall(Thread*, object, object, object, object) + { + return true; + } + + virtual void + shutDown(Thread*) + { + // ignore + } + + virtual void + dispose() + { + if (tzdata) { + tzdata->dispose(); + } + allocator->free(this, sizeof(*this)); + } + + Allocator* allocator; + System::Region* tzdata; +}; + +int64_t JNICALL +mapData(Thread* t, object, uintptr_t*) +{ + object c = resolveClass + (t, root(t, Machine::BootLoader), "libcore/io/MemoryMappedFile"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod(t, c, "", "(JJ)V"); + + const char* jar = "javahomeJar"; + Finder* finder = getFinder(t, jar, strlen(jar)); + if (finder) { + System::Region* r = finder->find("tzdata"); + if (r) { + MyClasspath* cp = static_cast(t->m->classpath); + + expect(t, cp->tzdata == 0); + + cp->tzdata = r; + + t->m->processor->invoke + (t, constructor, instance, reinterpret_cast(r->start()), + static_cast(r->length())); + + return reinterpret_cast(instance); + } + } + + throwNew(t, Machine::RuntimeExceptionType); +} + +void JNICALL +closeMemoryMappedFile(Thread* t, object method, uintptr_t* arguments) +{ + object file = reinterpret_cast(arguments[0]); + PROTECT(t, file); + + MyClasspath* cp = static_cast(t->m->classpath); + + if (cp->tzdata) { + object field = resolveField(t, objectClass(t, file), "address", "J"); + + if (fieldAtOffset(file, fieldOffset(t, field)) + == reinterpret_cast(cp->tzdata->start())) + { + cp->tzdata->dispose(); + cp->tzdata = 0; + + fieldAtOffset(file, fieldOffset(t, field)) = 0; + return; + } + } + + t->m->processor->invoke + (t, nativeInterceptOriginal + (t, methodRuntimeDataNative(t, getMethodRuntimeData(t, method))), + file); +} + +} // namespace local + +} // namespace + +namespace vm { + +Classpath* +makeClasspath(System*, Allocator* allocator, const char*, const char*) +{ + return new (allocator->allocate(sizeof(local::MyClasspath))) + local::MyClasspath(allocator); +} + +} // namespace vm + +extern "C" int +jniRegisterNativeMethods(JNIEnv* e, const char* className, + const JNINativeMethod* methods, int methodCount) +{ + jclass c = e->vtable->FindClass(e, className); + + if (c) { + e->vtable->RegisterNatives(e, c, methods, methodCount); + } else { + e->vtable->ExceptionClear(e); + } + + return 0; +} + +extern "C" void +jniLogException(JNIEnv*, int, const char*, jthrowable) +{ + // ignore +} + +extern "C" int +jniThrowException(JNIEnv* e, const char* className, const char* message) +{ + jclass c = e->vtable->FindClass(e, className); + + if (c) { + e->vtable->ThrowNew(e, c, message); + } + + return 0; +} + +extern "C" int +jniThrowExceptionFmt(JNIEnv* e, const char* className, const char* format, + va_list args) +{ + const unsigned size = 4096; + char buffer[size]; + ::vsnprintf(buffer, size, format, args); + return jniThrowException(e, className, buffer); +} + +extern "C" int +jniThrowNullPointerException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/NullPointerException", message); +} + +extern "C" int +jniThrowRuntimeException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/RuntimeException", message); +} + +extern "C" int +jniThrowIOException(JNIEnv* e, const char* message) +{ + return jniThrowException(e, "java/lang/IOException", message); +} + +extern "C" const char* +jniStrError(int error, char* buffer, size_t length) +{ +#ifdef PLATFORM_WINDOWS + const char* s = strerror(error); + if (strlen(s) < length) { + strncpy(buffer, s, length); + return buffer; + } else { + return 0; + } +#else + if (static_cast(strerror_r(error, buffer, length)) == 0) { + return buffer; + } else { + return 0; + } +#endif +} + +extern "C" int +__android_log_print(int priority, const char* tag, const char* format, ...) +{ + va_list a; + const unsigned size = 4096; + char buffer[size]; + + va_start(a, format); + ::vsnprintf(buffer, size, format, a); + va_end(a); + + return fprintf(stderr, "%d %s %s\n", priority, tag, buffer); +} + +extern "C" int +jniGetFDFromFileDescriptor(JNIEnv* e, jobject descriptor) +{ + return e->vtable->GetIntField + (e, descriptor, e->vtable->GetFieldID + (e, e->vtable->FindClass + (e, "java/io/FileDescriptor"), "descriptor", "I")); +} + +extern "C" void +jniSetFileDescriptorOfFD(JNIEnv* e, jobject descriptor, int value) +{ + e->vtable->SetIntField + (e, descriptor, e->vtable->GetFieldID + (e, e->vtable->FindClass + (e, "java/io/FileDescriptor"), "descriptor", "I"), value); +} + +extern "C" jobject +jniCreateFileDescriptor(JNIEnv* e, int fd) +{ + jobject descriptor = e->vtable->NewObject + (e, e->vtable->FindClass(e, "java/io/FileDescriptor"), + e->vtable->GetMethodID + (e, e->vtable->FindClass(e, "java/io/FileDescriptor"), "", "()V")); + + jniSetFileDescriptorOfFD(e, descriptor, fd); + + return descriptor; +} + +struct _JNIEnv; + +int +register_org_apache_harmony_dalvik_NativeTestTarget(_JNIEnv*) +{ + // ignore + return 0; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_compareTo +(Thread* t, object, uintptr_t* arguments) +{ + object a = reinterpret_cast(arguments[0]); + object b = reinterpret_cast(arguments[1]); + + unsigned length = stringLength(t, a); + if (length > stringLength(t, b)) { + length = stringLength(t, b); + } + + for (unsigned i = 0; i < length; ++i) { + int d = stringCharAt(t, a, i) - stringCharAt(t, b, i); + if (d) { + return d; + } + } + + return stringLength(t, a) - stringLength(t, b); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_isEmpty +(Thread* t, object, uintptr_t* arguments) +{ + return stringLength(t, reinterpret_cast(arguments[0])) == 0; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_length +(Thread* t, object, uintptr_t* arguments) +{ + return stringLength(t, reinterpret_cast(arguments[0])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_charAt +(Thread* t, object, uintptr_t* arguments) +{ + return stringCharAt(t, reinterpret_cast(arguments[0]), arguments[1]); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_equals +(Thread* t, object, uintptr_t* arguments) +{ + return arguments[1] and stringEqual + (t, reinterpret_cast(arguments[0]), + reinterpret_cast(arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_String_fastIndexOf +(Thread* t, object, uintptr_t* arguments) +{ + object s = reinterpret_cast(arguments[0]); + unsigned c = arguments[1]; + unsigned start = arguments[2]; + + for (unsigned i = start; i < stringLength(t, s); ++i) { + if (stringCharAt(t, s, i) == c) { + return i; + } + } + + return -1; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getInterfaces +(Thread* t, object, uintptr_t* arguments) +{ + object c = reinterpret_cast(arguments[0]); + + object addendum = classAddendum(t, jclassVmClass(t, c)); + if (addendum) { + object table = classAddendumInterfaceTable(t, addendum); + if (table) { + PROTECT(t, table); + + object array = makeObjectArray(t, arrayLength(t, table)); + PROTECT(t, array); + + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + object c = getJClass(t, arrayBody(t, table, i)); + set(t, array, ArrayBody + (i * BytesPerWord), c); + } + + return reinterpret_cast(array); + } + } + + return reinterpret_cast + (makeObjectArray(t, type(t, Machine::JclassType), 0)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_newInstanceImpl +(Thread* t, object, uintptr_t* arguments) +{ + object c = jclassVmClass(t, reinterpret_cast(arguments[0])); + + object method = resolveMethod(t, c, "", "()V"); + PROTECT(t, method); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + t->m->processor->invoke(t, method, instance); + + return reinterpret_cast(instance); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getComponentType +(Thread* t, object, uintptr_t* arguments) +{ + object c = reinterpret_cast(arguments[0]); + + if (classArrayDimensions(t, jclassVmClass(t, c))) { + uint8_t n = byteArrayBody(t, className(t, jclassVmClass(t, c)), 1); + if (n != 'L' and n != '[') { + return reinterpret_cast + (getJClass(t, primitiveClass(t, n))); + } else { + return reinterpret_cast + (getJClass(t, classStaticTable(t, jclassVmClass(t, c)))); + } + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_classForName +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + object loader = reinterpret_cast(arguments[2]); + PROTECT(t, loader); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "forName", + "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); + + return reinterpret_cast + (t->m->processor->invoke + (t, method, 0, name, static_cast(arguments[1]), loader)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getDeclaredField +(Thread* t, object, uintptr_t* arguments) +{ + object c = reinterpret_cast(arguments[0]); + PROTECT(t, c); + + object name = reinterpret_cast(arguments[1]); + PROTECT(t, name); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "findField", + "(Lavian/VMClass;Ljava/lang/String;)I"); + + int index = intValue + (t, t->m->processor->invoke + (t, method, 0, jclassVmClass(t, c), name)); + + if (index >= 0) { + return reinterpret_cast(local::makeField(t, c, index)); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getDeclaredConstructorOrMethod +(Thread* t, object, uintptr_t* arguments) +{ + object c = reinterpret_cast(arguments[0]); + PROTECT(t, c); + + object name = reinterpret_cast(arguments[1]); + PROTECT(t, name); + + object parameterTypes = reinterpret_cast(arguments[2]); + PROTECT(t, parameterTypes); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "findMethod", + "(Lavian/VMClass;Ljava/lang/String;[Ljava/lang/Class;)I"); + + int index = intValue + (t, t->m->processor->invoke + (t, method, 0, jclassVmClass(t, c), name, parameterTypes)); + + if (index >= 0) { + return reinterpret_cast + (local::makeMethodOrConstructor(t, c, index)); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_SystemClassLoader_findLoadedVMClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_findLoadedClass +(Thread* t, object method, uintptr_t* arguments) +{ + int64_t v = Avian_avian_SystemClassLoader_findLoadedVMClass + (t, method, arguments); + + if (v) { + return reinterpret_cast + (getJClass(t, reinterpret_cast(v))); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_defineVMClass +(Thread*, object, uintptr_t*); + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMClassLoader_defineClass__Ljava_lang_ClassLoader_2Ljava_lang_String_2_3BII +(Thread* t, object method, uintptr_t* arguments) +{ + uintptr_t args[] + = { arguments[0], arguments[2], arguments[3], arguments[4] }; + + int64_t v = Avian_avian_Classes_defineVMClass(t, method, args); + + if (v) { + return reinterpret_cast + (getJClass(t, reinterpret_cast(v))); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_bootClassPath +(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(root(t, Machine::BootLoader)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_classPath +(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(root(t, Machine::AppLoader)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_vmVersion +(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(makeString(t, "%s", AVIAN_VERSION)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_properties +(Thread* t, object, uintptr_t*) +{ + object array = makeObjectArray(t, type(t, Machine::StringType), 1); + PROTECT(t, array); + + object property = makeString(t, "java.protocol.handler.pkgs=avian"); + + set(t, array, ArrayBody, property); + + return reinterpret_cast(array); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Runtime_gc +(Thread* t, object, uintptr_t*) +{ + collect(t, Heap::MajorCollection); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Runtime_nativeLoad +(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[0]); + PROTECT(t, name); + + unsigned length = stringLength(t, name); + THREAD_RUNTIME_ARRAY(t, char, n, length + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(n)); + + if (loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), false, true)) { + return 0; + } else { + return reinterpret_cast(name); + } +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_System_arraycopy +(Thread* t, object, uintptr_t* arguments) +{ + arrayCopy(t, reinterpret_cast(arguments[0]), + arguments[1], + reinterpret_cast(arguments[2]), + arguments[3], + arguments[4]); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_sun_misc_Unsafe_objectFieldOffset +(Thread* t, object, uintptr_t* arguments) +{ + object jfield = reinterpret_cast(arguments[1]); + return fieldOffset + (t, arrayBody + (t, classFieldTable + (t, jclassVmClass(t, jfieldDeclaringClass(t, jfield))), + jfieldSlot(t, jfield))); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMThread_interrupt +(Thread* t, object, uintptr_t* arguments) +{ + object vmThread = reinterpret_cast(arguments[0]); + PROTECT(t, vmThread); + + object field = resolveField + (t, objectClass(t, vmThread), "thread", "Ljava/lang/Thread;"); + + interrupt + (t, reinterpret_cast + (threadPeer(t, fieldAtOffset(vmThread, fieldOffset(t, field))))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMThread_interrupted +(Thread* t, object, uintptr_t*) +{ + return getAndClearInterrupted(t, t); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMThread_isInterrupted +(Thread* t, object, uintptr_t* arguments) +{ + object vmThread = reinterpret_cast(arguments[0]); + PROTECT(t, vmThread); + + object field = resolveField + (t, objectClass(t, vmThread), "thread", "Ljava/lang/Thread;"); + + return threadInterrupted + (t, fieldAtOffset(vmThread, fieldOffset(t, field))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMThread_getStatus +(Thread*, object, uintptr_t*) +{ + // todo + return -1; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_VMThread_currentThread +(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(t->javaThread); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMThread_create +(Thread* t, object, uintptr_t* arguments) +{ + object thread = reinterpret_cast(arguments[0]); + PROTECT(t, thread); + + local::initVmThread(t, thread); + startThread(t, thread); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_VMThread_sleep +(Thread* t, object, uintptr_t* arguments) +{ + int64_t milliseconds; memcpy(&milliseconds, arguments, 8); + if (arguments[2] > 0) ++ milliseconds; + if (milliseconds <= 0) milliseconds = 1; + + if (threadSleepLock(t, t->javaThread) == 0) { + object lock = makeJobject(t); + set(t, t->javaThread, ThreadSleepLock, lock); + } + + acquire(t, threadSleepLock(t, t->javaThread)); + vm::wait(t, threadSleepLock(t, t->javaThread), milliseconds); + release(t, threadSleepLock(t, t->javaThread)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMStack_getThreadStackTrace +(Thread* t, object, uintptr_t* arguments) +{ + Thread* p = reinterpret_cast + (threadPeer(t, reinterpret_cast(arguments[0]))); + + return reinterpret_cast + (local::translateStackTrace + (t, p == t + ? makeTrace(t) + : t->m->processor->getStackTrace(t, p))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMStack_getCallingClassLoader +(Thread* t, object, uintptr_t*) +{ + class Visitor: public Processor::StackVisitor { + public: + Visitor(Thread* t): + t(t), loader(0), counter(2) + { } + + virtual bool visit(Processor::StackWalker* walker) { + if (counter--) { + return true; + } else { + this->loader = classLoader(t, methodClass(t, walker->method())); + return false; + } + } + + Thread* t; + object loader; + unsigned counter; + } v(t); + + t->m->processor->walkStack(t, &v); + + return reinterpret_cast(v.loader); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_min +(Thread*, object, uintptr_t* arguments) +{ + return min(static_cast(arguments[0]), static_cast(arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_max +(Thread*, object, uintptr_t* arguments) +{ + return max(static_cast(arguments[0]), static_cast(arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_cos +(Thread*, object, uintptr_t* arguments) +{ + int64_t v; memcpy(&v, arguments, 8); + return doubleToBits(cos(bitsToDouble(v))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_sin +(Thread*, object, uintptr_t* arguments) +{ + int64_t v; memcpy(&v, arguments, 8); + return doubleToBits(sin(bitsToDouble(v))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_sqrt +(Thread*, object, uintptr_t* arguments) +{ + int64_t v; memcpy(&v, arguments, 8); + return doubleToBits(sqrt(bitsToDouble(v))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_abs__I +(Thread*, object, uintptr_t* arguments) +{ + return abs(static_cast(arguments[0])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_abs__J +(Thread*, object, uintptr_t* arguments) +{ + return llabs(arguments[0]); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Math_abs__F +(Thread*, object, uintptr_t* arguments) +{ + return floatToBits(abs(bitsToFloat(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Float_intBitsToFloat +(Thread*, object, uintptr_t* arguments) +{ + return arguments[0]; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Float_floatToIntBits +(Thread*, object, uintptr_t* arguments) +{ + if (((arguments[0] & 0x7F800000) == 0x7F800000) + and ((arguments[0] & 0x007FFFFF) != 0)) + { + return 0x7fc00000; + } else { + return arguments[0]; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Double_doubleToRawLongBits +(Thread*, object, uintptr_t* arguments) +{ + int64_t v; memcpy(&v, arguments, 8); + // todo: do we need to do NaN checks as in + // Avian_java_lang_Float_floatToIntBits above? If so, update + // Double.doubleToRawLongBits in the Avian class library too. + return v; +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Object_wait +(Thread* t, object, uintptr_t* arguments) +{ + jlong milliseconds; memcpy(&milliseconds, arguments + 1, sizeof(jlong)); + + wait(t, reinterpret_cast(arguments[0]), milliseconds); +} + +extern "C" JNIEXPORT void JNICALL +Avian_java_lang_Object_notifyAll +(Thread* t, object, uintptr_t* arguments) +{ + notifyAll(t, reinterpret_cast(arguments[0])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_getClass +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (getJClass(t, objectClass(t, reinterpret_cast(arguments[0])))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_hashCode +(Thread* t, object, uintptr_t* arguments) +{ + return objectHash(t, reinterpret_cast(arguments[0])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Object_internalClone +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (clone(t, reinterpret_cast(arguments[1]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getModifiers +(Thread* t, object, uintptr_t* arguments) +{ + return classFlags + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getSuperclass +(Thread* t, object, uintptr_t* arguments) +{ + object c = jclassVmClass(t, reinterpret_cast(arguments[0])); + if (classFlags(t, c) & ACC_INTERFACE) { + return 0; + } else { + object s = classSuper(t, c); + return s ? reinterpret_cast(getJClass(t, s)) : 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_desiredAssertionStatus +(Thread*, object, uintptr_t*) +{ + return 1; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getNameNative +(Thread* t, object, uintptr_t* arguments) +{ + object name = className + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))); + + THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, name)); + replace('/', '.', RUNTIME_ARRAY_BODY(s), + reinterpret_cast(&byteArrayBody(t, name, 0))); + + return reinterpret_cast + (makeString(t, "%s", RUNTIME_ARRAY_BODY(s))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_isInterface +(Thread* t, object, uintptr_t* arguments) +{ + return (classFlags + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))) + & ACC_INTERFACE) != 0; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_isPrimitive +(Thread* t, object, uintptr_t* arguments) +{ + return (classVmFlags + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))) + & PrimitiveFlag) != 0; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_getClassLoader +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (classLoader + (t, jclassVmClass(t, reinterpret_cast(arguments[0])))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_isAssignableFrom +(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + object that = reinterpret_cast(arguments[1]); + + if (LIKELY(that)) { + return isAssignableFrom + (t, jclassVmClass(t, this_), jclassVmClass(t, that)); + } else { + throwNew(t, Machine::NullPointerExceptionType); + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Class_isInstance +(Thread* t, object, uintptr_t* arguments) +{ + object this_ = reinterpret_cast(arguments[0]); + object o = reinterpret_cast(arguments[1]); + + if (o) { + return instanceOf(t, jclassVmClass(t, this_), o); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_invokeNative +(Thread* t, object, uintptr_t* arguments) +{ + object instance = reinterpret_cast(arguments[1]); + object args = reinterpret_cast(arguments[2]); + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[3]))), + arguments[6]); + + return reinterpret_cast(invoke(t, method, instance, args)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_getMethodModifiers +(Thread* t, object, uintptr_t* arguments) +{ + return methodFlags + (t, arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_isAnnotationPresent +(Thread* t, object, uintptr_t* arguments) +{ + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1]); + + object addendum = methodAddendum(t, method); + if (addendum) { + object table = addendumAnnotationTable(t, addendum); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + if (objectArrayBody(t, objectArrayBody(t, table, i), 1) + == reinterpret_cast(arguments[2])) + { + return true; + } + } + } + } + + return false; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_getAnnotation +(Thread* t, object, uintptr_t* arguments) +{ + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1]); + + object addendum = methodAddendum(t, method); + if (addendum) { + object table = addendumAnnotationTable(t, addendum); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + if (objectArrayBody(t, objectArrayBody(t, table, i), 1) + == reinterpret_cast(arguments[2])) + { + PROTECT(t, method); + PROTECT(t, table); + + object get = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "getAnnotation", + "(Ljava/lang/ClassLoader;[Ljava/lang/Object;)" + "Ljava/lang/annotation/Annotation;"); + + return reinterpret_cast + (t->m->processor->invoke + (t, get, 0, classLoader(t, methodClass(t, method)), + objectArrayBody(t, table, i))); + } + } + } + } + + return false; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Method_getDeclaredAnnotations +(Thread* t, object, uintptr_t* arguments) +{ + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1]); + + object addendum = methodAddendum(t, method); + if (addendum) { + object table = addendumAnnotationTable(t, addendum); + if (table) { + PROTECT(t, method); + PROTECT(t, table); + + object array = makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), "java/lang/annotation/Annotation"), + objectArrayLength(t, table)); + PROTECT(t, array); + + object get = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Classes", "getAnnotation", + "(Ljava/lang/ClassLoader;[Ljava/lang/Object;)" + "Ljava/lang/annotation/Annotation;"); + PROTECT(t, get); + + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + object a = t->m->processor->invoke + (t, get, 0, classLoader(t, methodClass(t, method)), + objectArrayBody(t, table, i)); + + set(t, array, ArrayBody + (i * BytesPerWord), a); + } + + return reinterpret_cast(array); + } + } + + return reinterpret_cast + (makeObjectArray + (t, resolveClass + (t, root(t, Machine::BootLoader), "java/lang/annotation/Annotation"), + 0)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Constructor_constructNative +(Thread* t, object, uintptr_t* arguments) +{ + object args = reinterpret_cast(arguments[1]); + PROTECT(t, args); + + object c = jclassVmClass(t, reinterpret_cast(arguments[2])); + + object method = arrayBody(t, classMethodTable(t, c), arguments[4]); + PROTECT(t, method); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + t->m->processor->invokeArray(t, method, instance, args); + + return reinterpret_cast(instance); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getField +(Thread* t, object, uintptr_t* arguments) +{ + object field = arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[2]))), + arguments[4]); + + if (fieldFlags(t, field) & ACC_STATIC) { + return reinterpret_cast + (fieldAtOffset + (classStaticTable(t, fieldClass(t, field)), fieldOffset(t, field))); + } else { + return reinterpret_cast + (fieldAtOffset + (reinterpret_cast(arguments[1]), fieldOffset(t, field))); + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Field_getFieldModifiers +(Thread* t, object, uintptr_t* arguments) +{ + return fieldFlags + (t, arrayBody + (t, classFieldTable + (t, jclassVmClass(t, reinterpret_cast(arguments[1]))), + arguments[2])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Throwable_nativeFillInStackTrace +(Thread* t, object, uintptr_t*) +{ + return reinterpret_cast(getTrace(t, 2)); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Throwable_nativeGetStackTrace +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (local::translateStackTrace(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_makeMethod +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (local::makeMethodOrConstructor + (t, reinterpret_cast(arguments[0]), arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_reflect_Array_createObjectArray +(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast + (makeObjectArray + (t, jclassVmClass(t, reinterpret_cast(arguments[0])), + arguments[1])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_nio_ByteOrder_isLittleEndian +(Thread*, object, uintptr_t*) +{ +#ifdef ARCH_powerpc + return false; +#else + return true; +#endif +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_newNonMovableArray +(Thread* t, object, uintptr_t* arguments) +{ + if (jclassVmClass(t, reinterpret_cast(arguments[1])) + == type(t, Machine::JbyteType)) + { + object array = allocate3 + (t, t->m->heap, Machine::FixedAllocation, + ArrayBody + arguments[2], false); + + setObjectClass(t, array, type(t, Machine::ByteArrayType)); + byteArrayLength(t, array) = arguments[2]; + + return reinterpret_cast(array); + } else { + // todo + abort(t); + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_dalvik_system_VMRuntime_addressOf +(Thread*, object, uintptr_t* arguments) +{ + return arguments[1] + ArrayBody; +} + +extern "C" JNIEXPORT void JNICALL +Avian_libcore_io_Memory_pokeLong +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int64_t v; memcpy(&v, arguments + 2, 8); + if (arguments[4]) { + v = swapV8(v); + } + memcpy(reinterpret_cast(address), &v, 8); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Memory_peekLong +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int64_t v; memcpy(&v, reinterpret_cast(address), 8); + return arguments[2] ? swapV8(v) : v; +} + +extern "C" JNIEXPORT void JNICALL +Avian_libcore_io_Memory_pokeInt +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int32_t v = arguments[3] ? swapV4(arguments[2]) : arguments[2]; + memcpy(reinterpret_cast(address), &v, 4); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Memory_peekInt +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int32_t v; memcpy(&v, reinterpret_cast(address), 4); + return arguments[2] ? swapV4(v) : v; +} + +extern "C" JNIEXPORT void JNICALL +Avian_libcore_io_Memory_pokeShort +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int16_t v = arguments[3] ? swapV2(arguments[2]) : arguments[2]; + memcpy(reinterpret_cast(address), &v, 2); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Memory_peekShort +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + int16_t v; memcpy(&v, reinterpret_cast(address), 2); + return arguments[2] ? swapV2(v) : v; +} + +extern "C" JNIEXPORT void JNICALL +Avian_libcore_io_Memory_pokeByte +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + *reinterpret_cast(address) = arguments[2]; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Memory_peekByte +(Thread*, object, uintptr_t* arguments) +{ + int64_t address; memcpy(&address, arguments, 8); + return *reinterpret_cast(address); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_nanoTime +(Thread* t, object, uintptr_t*) +{ + return t->m->system->now() * 1000 * 1000; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_currentTimeMillis +(Thread* t, object, uintptr_t*) +{ + return t->m->system->now(); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_identityHashCode +(Thread* t, object, uintptr_t* arguments) +{ + return objectHash(t, reinterpret_cast(arguments[0])); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_System_mapLibraryName +(Thread* t, object, uintptr_t* arguments) +{ + object original = reinterpret_cast(arguments[0]); + unsigned originalLength = stringUTFLength(t, original); + THREAD_RUNTIME_ARRAY(t, char, originalChars, originalLength); + stringUTFChars + (t, original, RUNTIME_ARRAY_BODY(originalChars), originalLength); + + return reinterpret_cast + (makeString(t, "%s%.*s%s", t->m->system->libraryPrefix(), originalLength, + RUNTIME_ARRAY_BODY(originalChars), + t->m->system->librarySuffix())); +} + +#ifdef PLATFORM_WINDOWS + +# include + +void register_java_io_Console(_JNIEnv*) { } +void register_java_lang_ProcessManager(_JNIEnv*) { } +void register_libcore_io_OsConstants(_JNIEnv*) { } +void register_libcore_io_AsynchronousCloseMonitor(_JNIEnv*) { } +void register_libcore_io_Posix(_JNIEnv*) { } +void register_libcore_net_RawSocket(_JNIEnv*) { } +void register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(_JNIEnv*) { } + +extern "C" JNIEXPORT void JNICALL +Avian_libcore_io_OsConstants_initConstants +(Thread* t, object method, uintptr_t*) +{ + object c = methodClass(t, method); + PROTECT(t, c); + + object table = classStaticTable(t, c); + PROTECT(t, table); + + object field = resolveField(t, c, "STDIN_FILENO", "I"); + fieldAtOffset(table, fieldOffset(t, field)) = 0; + + field = resolveField(t, c, "STDOUT_FILENO", "I"); + fieldAtOffset(table, fieldOffset(t, field)) = 1; + + field = resolveField(t, c, "STDERR_FILENO", "I"); + fieldAtOffset(table, fieldOffset(t, field)) = 2; +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Posix_getenv(Thread* t, object, uintptr_t* arguments) +{ + object name = reinterpret_cast(arguments[1]); + + THREAD_RUNTIME_ARRAY(t, uint16_t, chars, stringLength(t, name) + 1); + stringChars(t, name, RUNTIME_ARRAY_BODY(chars)); + + wchar_t* value = _wgetenv + (reinterpret_cast(RUNTIME_ARRAY_BODY(chars))); + + if (value) { + unsigned size = wcslen(value); + + object a = makeCharArray(t, size); + if (size) { + memcpy(&charArrayBody(t, a, 0), value, size * sizeof(jchar)); + } + + return reinterpret_cast + (t->m->classpath->makeString(t, a, 0, size)); + } else { + return 0; + } +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Posix_uname(Thread* t, object, uintptr_t*) +{ + object c = resolveClass + (t, root(t, Machine::BootLoader), "libcore/io/StructUtsname"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + +#ifdef ARCH_x86_32 + object arch = makeString(t, "x86"); +#elif defined ARCH_x86_64 + object arch = makeString(t, "x86_64"); +#elif defined ARCH_powerpc + object arch = makeString(t, "ppc"); +#elif defined ARCH_arm + object arch = makeString(t, "arm"); +#else + object arch = makeString(t, "unknown"); +#endif + + set(t, instance, fieldOffset + (t, resolveField(t, c, "machine", "Ljava/lang/String;")), arch); + + object platform = makeString(t, "Windows"); + + set(t, instance, fieldOffset + (t, resolveField(t, c, "sysname", "Ljava/lang/String;")), platform); + + object version = makeString(t, "unknown"); + + set(t, instance, fieldOffset + (t, resolveField(t, c, "release", "Ljava/lang/String;")), version); + + return reinterpret_cast(instance); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_libcore_io_Posix_writeBytes(Thread* t, object, uintptr_t* arguments) +{ + object fd = reinterpret_cast(arguments[1]); + PROTECT(t, fd); + + object buffer = reinterpret_cast(arguments[2]); + PROTECT(t, buffer); + + int offset = arguments[3]; + int count = arguments[4]; + + int d = jniGetFDFromFileDescriptor(t, &fd); + + int r; + if (objectClass(t, buffer) == type(t, Machine::ByteArrayType)) { + void* tmp = t->m->heap->allocate(count); + memcpy(tmp, &byteArrayBody(t, buffer, offset), count); + { ENTER(t, Thread::IdleState); + r = _write(d, tmp, count); + } + t->m->heap->free(tmp, count); + } else { + void* p = local::getDirectBufferAddress(t, buffer); + { ENTER(t, Thread::IdleState); + r = _write(d, p, count); + } + } + + if (r < 0) { + THREAD_RUNTIME_ARRAY(t, char, message, 256); + throwNew(t, Machine::RuntimeExceptionType, "writeBytes %d: %s", d, + jniStrError(errno, RUNTIME_ARRAY_BODY(message), 0)); + } else { + return r; + } +} + +#endif diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 5e68d2ed14..ef16a767c1 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -8,9 +8,11 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "classpath-common.h" -#include "process.h" +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/process.h" + +#include using namespace vm; @@ -54,6 +56,37 @@ class MyClasspath : public Classpath { root(t, Machine::BootLoader), 0, 0, group, 0); } + virtual object + makeJMethod(Thread* t, object vmMethod) + { + PROTECT(t, vmMethod); + + object jmethod = makeJmethod(t, vmMethod, false); + + return byteArrayBody(t, methodName(t, vmMethod), 0) == '<' + ? makeJconstructor(t, jmethod) : jmethod; + } + + virtual object + getVMMethod(Thread* t, object jmethod) + { + return objectClass(t, jmethod) == type(t, Machine::JmethodType) + ? jmethodVmMethod(t, jmethod) + : jmethodVmMethod(t, jconstructorMethod(t, jmethod)); + } + + virtual object + makeJField(Thread* t, object vmField) + { + return makeJfield(t, vmField, false); + } + + virtual object + getVMField(Thread* t, object jfield) + { + return jfieldVmField(t, jfield); + } + virtual void clearInterrupted(Thread*) { @@ -76,6 +109,18 @@ class MyClasspath : public Classpath { vm::resolveNative(t, method); } + virtual void + interceptMethods(Thread*) + { + // ignore + } + + virtual void + preBoot(Thread*) + { + // ignore + } + virtual void boot(Thread*) { @@ -88,6 +133,65 @@ class MyClasspath : public Classpath { return AVIAN_CLASSPATH; } + virtual void + updatePackageMap(Thread*, object) + { + // ignore + } + + virtual object + makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke + (t, constructor, instance, reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* + getDirectBufferAddress(Thread* t, object b) + { + PROTECT(t, b); + + object field = resolveField(t, objectClass(t, b), "address", "J"); + + return reinterpret_cast + (fieldAtOffset(b, fieldOffset(t, field))); + } + + virtual int64_t + getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + object field = resolveField + (t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, fieldOffset(t, field)); + } + + virtual bool + canTailCall(Thread*, object, object, object, object) + { + return true; + } + + virtual void + shutDown(Thread*) + { + // ignore + } + virtual void dispose() { @@ -213,21 +317,21 @@ Avian_java_lang_reflect_Field_getPrimitive switch (code) { case ByteField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case BooleanField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case CharField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case ShortField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case IntField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case LongField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case FloatField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); case DoubleField: - return cast(instance, offset); + return fieldAtOffset(instance, offset); default: abort(t); } @@ -240,7 +344,7 @@ Avian_java_lang_reflect_Field_getObject object instance = reinterpret_cast(arguments[0]); int offset = arguments[1]; - return reinterpret_cast(cast(instance, offset)); + return reinterpret_cast(fieldAtOffset(instance, offset)); } extern "C" JNIEXPORT void JNICALL @@ -254,28 +358,28 @@ Avian_java_lang_reflect_Field_setPrimitive switch (code) { case ByteField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case BooleanField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case CharField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case ShortField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case IntField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case LongField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case FloatField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; case DoubleField: - cast(instance, offset) = static_cast(value); + fieldAtOffset(instance, offset) = static_cast(value); break; default: abort(t); @@ -342,7 +446,7 @@ Avian_java_lang_reflect_Array_getLength unsigned elementSize = classArrayElementSize(t, objectClass(t, array)); if (LIKELY(elementSize)) { - return cast(array, BytesPerWord); + return fieldAtOffset(array, BytesPerWord); } else { throwNew(t, Machine::IllegalArgumentExceptionType); } @@ -629,7 +733,7 @@ Avian_avian_Atomic_compareAndSwapObject uintptr_t update = arguments[4]; bool success = atomicCompareAndSwap - (&cast(target, offset), expect, update); + (&fieldAtOffset(target, offset), expect, update); if (success) { mark(t, target, offset); @@ -638,42 +742,6 @@ Avian_avian_Atomic_compareAndSwapObject return success; } -extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_Classes_primitiveClass -(Thread* t, object, uintptr_t* arguments) -{ - return reinterpret_cast(primitiveClass(t, arguments[0])); -} - -extern "C" JNIEXPORT int64_t JNICALL -Avian_avian_Classes_defineVMClass -(Thread* t, object, uintptr_t* arguments) -{ - object loader = reinterpret_cast(arguments[0]); - object b = reinterpret_cast(arguments[1]); - int offset = arguments[2]; - int length = arguments[3]; - - uint8_t* buffer = static_cast - (t->m->heap->allocate(length)); - - THREAD_RESOURCE2(t, uint8_t*, buffer, int, length, - t->m->heap->free(buffer, length)); - - memcpy(buffer, &byteArrayBody(t, b, offset), length); - - return reinterpret_cast(defineClass(t, loader, buffer, length)); -} - -extern "C" JNIEXPORT void JNICALL -Avian_avian_Classes_initialize -(Thread* t, object, uintptr_t* arguments) -{ - object this_ = reinterpret_cast(arguments[0]); - - initClass(t, this_); -} - extern "C" JNIEXPORT int64_t JNICALL Avian_avian_Classes_isAssignableFrom (Thread* t, object, uintptr_t* arguments) @@ -695,3 +763,41 @@ Avian_avian_Classes_getVMClass return reinterpret_cast (objectClass(t, reinterpret_cast(arguments[0]))); } + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Classes_makeMethod +(Thread* t, object, uintptr_t* arguments) +{ + object method = arrayBody + (t, classMethodTable + (t, jclassVmClass(t, reinterpret_cast(arguments[0]))), + arguments[1]); + PROTECT(t, method); + + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/reflect/Method"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod(t, c, "", "(Lavian/VMMethod;)V"); + + t->m->processor->invoke(t, constructor, instance, method); + + if (byteArrayBody(t, methodName(t, method), 0) == '<') { + method = instance; + + c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/reflect/Constructor"); + + object instance = makeNew(t, c); + + object constructor = resolveMethod + (t, c, "", "(Ljava/lang/Method;)V"); + + t->m->processor->invoke(t, constructor, instance, method); + } + + return reinterpret_cast(instance); +} diff --git a/src/classpath-common.h b/src/classpath-common.h deleted file mode 100644 index d8105b9cd7..0000000000 --- a/src/classpath-common.h +++ /dev/null @@ -1,334 +0,0 @@ -/* Copyright (c) 2010-2012, 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. */ - -#ifndef CLASSPATH_COMMON_H -#define CLASSPATH_COMMON_H - -#include "tokenizer.h" - -namespace vm { - -object -getTrace(Thread* t, unsigned skipCount) -{ - class Visitor: public Processor::StackVisitor { - public: - Visitor(Thread* t, int skipCount): - t(t), trace(0), skipCount(skipCount) - { } - - virtual bool visit(Processor::StackWalker* walker) { - if (skipCount == 0) { - object method = walker->method(); - if (isAssignableFrom - (t, type(t, Machine::ThrowableType), methodClass(t, method)) - and vm::strcmp(reinterpret_cast(""), - &byteArrayBody(t, methodName(t, method), 0)) - == 0) - { - return true; - } else { - trace = makeTrace(t, walker); - return false; - } - } else { - -- skipCount; - return true; - } - } - - Thread* t; - object trace; - unsigned skipCount; - } v(t, skipCount); - - t->m->processor->walkStack(t, &v); - - if (v.trace == 0) v.trace = makeObjectArray(t, 0); - - return v.trace; -} - -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 -arrayCopy(Thread* t, object src, int32_t srcOffset, object dst, - int32_t dstOffset, int32_t length) -{ - if (LIKELY(src and dst)) { - if (LIKELY(compatibleArrayTypes - (t, objectClass(t, src), objectClass(t, dst)))) - { - unsigned elementSize = classArrayElementSize(t, objectClass(t, src)); - - if (LIKELY(elementSize)) { - intptr_t sl = cast(src, BytesPerWord); - intptr_t dl = cast(dst, BytesPerWord); - if (LIKELY(length > 0)) { - if (LIKELY(srcOffset >= 0 and srcOffset + length <= sl and - dstOffset >= 0 and dstOffset + length <= dl)) - { - uint8_t* sbody = &cast(src, ArrayBody); - uint8_t* dbody = &cast(dst, ArrayBody); - if (src == dst) { - memmove(dbody + (dstOffset * elementSize), - sbody + (srcOffset * elementSize), - length * elementSize); - } else { - memcpy(dbody + (dstOffset * elementSize), - sbody + (srcOffset * elementSize), - length * elementSize); - } - - if (classObjectMask(t, objectClass(t, dst))) { - mark(t, dst, ArrayBody + (dstOffset * BytesPerWord), length); - } - - return; - } else { - throwNew(t, Machine::IndexOutOfBoundsExceptionType); - } - } else { - return; - } - } - } - } else { - throwNew(t, Machine::NullPointerExceptionType); - return; - } - - throwNew(t, Machine::ArrayStoreExceptionType); -} - -void -runOnLoadIfFound(Thread* t, System::Library* library) -{ - void* p = library->resolve("JNI_OnLoad"); - -#ifdef PLATFORM_WINDOWS - if (p == 0) { - p = library->resolve("_JNI_OnLoad@8"); - if (p == 0) { - p = library->resolve("JNI_OnLoad@8"); - } - } -#endif - - if (p) { - jint (JNICALL * JNI_OnLoad)(Machine*, void*); - memcpy(&JNI_OnLoad, &p, sizeof(void*)); - JNI_OnLoad(t->m, 0); - } -} - -System::Library* -loadLibrary(Thread* t, const char* name) -{ - ACQUIRE(t, t->m->classLock); - - System::Library* last = t->m->libraries; - for (System::Library* lib = t->m->libraries; lib; lib = lib->next()) { - if (lib->name() and ::strcmp(lib->name(), name) == 0) { - // already loaded - return lib; - } - last = lib; - } - - System::Library* lib; - if (t->m->system->success(t->m->system->load(&lib, name))) { - last->setNext(lib); - return lib; - } else { - return 0; - } -} - -System::Library* -loadLibrary(Thread* t, const char* path, const char* name, bool mapName, - bool runOnLoad) -{ - ACQUIRE(t, t->m->classLock); - - char* mappedName; - unsigned nameLength = strlen(name); - if (mapName) { - const char* builtins = findProperty(t, "avian.builtins"); - if (builtins) { - const char* s = builtins; - while (*s) { - if (::strncmp(s, name, nameLength) == 0 - and (s[nameLength] == ',' or s[nameLength] == 0)) - { - // library is built in to this executable - if (runOnLoad and not t->m->triedBuiltinOnLoad) { - t->m->triedBuiltinOnLoad = true; - // todo: release the classLock before calling this to - // avoid the possibility of deadlock: - runOnLoadIfFound(t, t->m->libraries); - } - return t->m->libraries; - } else { - while (*s and *s != ',') ++ s; - if (*s) ++ s; - } - } - } - - const char* prefix = t->m->system->libraryPrefix(); - const char* suffix = t->m->system->librarySuffix(); - unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix); - - mappedName = static_cast - (t->m->heap->allocate(mappedNameLength + 1)); - - snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix); - - name = mappedName; - nameLength = mappedNameLength; - } else { - mappedName = 0; - } - - THREAD_RESOURCE2 - (t, char*, mappedName, unsigned, nameLength, if (mappedName) { - t->m->heap->free(mappedName, nameLength + 1); - }); - - System::Library* lib = 0; - for (Tokenizer tokenizer(path, t->m->system->pathSeparator()); - tokenizer.hasMore();) - { - Tokenizer::Token token(tokenizer.next()); - - unsigned fullNameLength = token.length + 1 + nameLength; - THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1); - - snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1, - "%*s/%s", token.length, token.s, name); - - lib = loadLibrary(t, RUNTIME_ARRAY_BODY(fullName)); - if (lib) break; - } - - if (lib == 0) { - lib = loadLibrary(t, name); - } - - if (lib) { - if (runOnLoad) { - runOnLoadIfFound(t, lib); - } - } else { - throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s", - name); - } - - return lib; -} - -object -clone(Thread* t, object o) -{ - PROTECT(t, o); - - 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); - // clear any object header flags: - setObjectClass(t, o, objectClass(t, o)); - } else { - clone = make(t, class_); - memcpy(reinterpret_cast(clone) + 1, - reinterpret_cast(o) + 1, - size - BytesPerWord); - } - - return clone; -} - -object -makeStackTraceElement(Thread* t, object e) -{ - PROTECT(t, e); - - object class_ = className(t, methodClass(t, traceElementMethod(t, e))); - PROTECT(t, class_); - - THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, class_)); - replace('/', '.', RUNTIME_ARRAY_BODY(s), - reinterpret_cast(&byteArrayBody(t, class_, 0))); - class_ = makeString(t, "%s", s); - - object method = methodName(t, traceElementMethod(t, e)); - PROTECT(t, method); - - method = t->m->classpath->makeString - (t, method, 0, byteArrayLength(t, method) - 1); - - unsigned line = t->m->processor->lineNumber - (t, traceElementMethod(t, e), traceElementIp(t, e)); - - object file = classSourceFile(t, methodClass(t, traceElementMethod(t, e))); - file = file ? t->m->classpath->makeString - (t, file, 0, byteArrayLength(t, file) - 1) : 0; - - return makeStackTraceElement(t, class_, method, file, line); -} - -object -translateInvokeResult(Thread* t, unsigned returnCode, object o) -{ - switch (returnCode) { - case ByteField: - return makeByte(t, intValue(t, o)); - - case BooleanField: - return makeBoolean(t, intValue(t, o) != 0); - - case CharField: - return makeChar(t, intValue(t, o)); - - case ShortField: - return makeShort(t, intValue(t, o)); - - case FloatField: - return makeFloat(t, intValue(t, o)); - - case IntField: - case LongField: - case ObjectField: - case VoidField: - return o; - - case DoubleField: - return makeDouble(t, longValue(t, o)); - - default: - abort(t); - } -} - -} // namespace vm - -#endif//CLASSPATH_COMMON_H diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index a965fd676d..fcbf27ea57 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -8,10 +8,10 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "classpath-common.h" -#include "util.h" -#include "process.h" +#include "avian/machine.h" +#include "avian/classpath-common.h" +#include "avian/util.h" +#include "avian/process.h" #ifdef PLATFORM_WINDOWS @@ -46,7 +46,8 @@ # define O_RDONLY _O_RDONLY -# ifdef AVIAN_OPENJDK_SRC +# if (defined AVIAN_OPENJDK_SRC) \ + || ((defined __x86_64__) && (defined __MINGW32__)) # define EXPORT(x) x # else # define EXPORT(x) _##x @@ -338,59 +339,86 @@ makeClassNameString(Thread* t, object name) replace('/', '.', RUNTIME_ARRAY_BODY(s), reinterpret_cast(&byteArrayBody(t, name, 0))); - return makeString(t, "%s", s); + return makeString(t, "%s", RUNTIME_ARRAY_BODY(s)); } +object +makeJmethod(Thread* t, object vmMethod, int index = -1); + +object +makeJconstructor(Thread* t, object vmMethod, int index = -1); + +object +makeJfield(Thread* t, object vmField, int index = -1); + void -interceptFileOperations(Thread*); +interceptFileOperations(Thread*, bool); void clearInterrupted(Thread*); class MyClasspath : public Classpath { public: - static const unsigned BufferSize = 1024; - MyClasspath(System* s, Allocator* allocator, const char* javaHome, const char* embedPrefix): allocator(allocator), ranNetOnLoad(0), ranManagementOnLoad(0) { class StringBuilder { public: - StringBuilder(System* s, char* pointer, unsigned remaining): - s(s), pointer(pointer), remaining(remaining) + StringBuilder(System* s, Allocator* allocator): + s(s), + allocator(allocator), + bufferSize(1024), + buffer(static_cast(allocator->allocate(bufferSize))), + offset(0) { } + void ensure(unsigned capacity) { + if (capacity > bufferSize) { + unsigned size = max(bufferSize * 2, capacity); + char* b = static_cast(allocator->allocate(size)); + + if (offset) { + memcpy(b, buffer, offset); + } + + allocator->free(buffer, bufferSize); + + buffer = b; + bufferSize = size; + } + } + void append(const char* append) { unsigned length = strlen(append); - expect(s, remaining > length); + ensure(offset + length + 1); - strncpy(pointer, append, remaining); + strncpy(buffer + offset, append, length + 1); - remaining -= length; - pointer += length; + offset += length; } void append(char c) { - assert(s, remaining > 1); + ensure(2); - pointer[0] = c; - pointer[1] = 0; + buffer[offset] = c; + buffer[offset + 1] = 0; - -- remaining; - ++ pointer; + ++ offset; } System* s; - char* pointer; - unsigned remaining; - } sb(s, buffer, BufferSize); + Allocator* allocator; + unsigned bufferSize; + char* buffer; + unsigned offset; + } sb(s, allocator); - this->javaHome = sb.pointer; + unsigned javaHomeOffset = sb.offset; sb.append(javaHome); sb.append('\0'); - this->classpath = sb.pointer; + unsigned classpathOffset = sb.offset; sb.append(AVIAN_CLASSPATH); sb.append(s->pathSeparator()); sb.append(javaHome); @@ -409,31 +437,47 @@ class MyClasspath : public Classpath { sb.append("/lib/resources.jar"); sb.append('\0'); - this->libraryPath = sb.pointer; + unsigned libraryPathOffset = sb.offset; sb.append(javaHome); #ifdef PLATFORM_WINDOWS - sb.append("/bin"); +# define LIB_DIR "/bin" #elif defined __APPLE__ - sb.append("/lib"); +# define LIB_DIR "/lib" #elif defined ARCH_x86_64 - sb.append("/lib/amd64"); +# define LIB_DIR "/lib/amd64" #elif defined ARCH_arm - sb.append("/lib/arm"); +# define LIB_DIR "/lib/arm" #else // todo: handle other architectures - sb.append("/lib/i386"); +# define LIB_DIR "/lib/i386" +#endif + +#ifdef PLATFORM_WINDOWS + sb.append(LIB_DIR); +#else + sb.append(LIB_DIR ":"); + sb.append(javaHome); + sb.append(LIB_DIR "/xawt"); #endif sb.append('\0'); - - this->tzMappings = sb.pointer; + + unsigned tzMappingsOffset = sb.offset; sb.append(javaHome); sb.append("/lib/tzmappings"); - this->tzMappingsLength = sb.pointer - tzMappings; + this->tzMappingsLength = sb.offset - tzMappingsOffset; sb.append('\0'); - this->embedPrefix = sb.pointer; + unsigned embedPrefixOffset = sb.offset; sb.append(embedPrefix); - this->embedPrefixLength = sb.pointer - this->embedPrefix; + this->embedPrefixLength = sb.offset - embedPrefixOffset; + + this->javaHome = sb.buffer + javaHomeOffset; + this->classpath = sb.buffer + classpathOffset; + this->libraryPath = sb.buffer + libraryPathOffset; + this->tzMappings = sb.buffer + tzMappingsOffset; + this->embedPrefix = sb.buffer + embedPrefixOffset; + this->buffer = sb.buffer; + this->bufferSize = sb.bufferSize; } virtual object @@ -460,6 +504,24 @@ class MyClasspath : public Classpath { object charArray = makeCharArray(t, length); for (int i = 0; i < length; ++i) { + if (byteArrayBody(t, array, offset + i) & 0x80) { + object constructor = resolveMethod + (t, type(t, Machine::StringType), "", + "([BIILjava/lang/String;)V"); + PROTECT(t, constructor); + + object utf8 = vm::makeString(t, "UTF8"); + PROTECT(t, utf8); + + object s = makeNew(t, type(t, Machine::StringType)); + PROTECT(t, s); + + t->m->processor->invoke + (t, constructor, s, array, offset, length, utf8); + + return s; + } + charArrayBody(t, charArray, i) = byteArrayBody(t, array, offset + i); } @@ -511,6 +573,44 @@ class MyClasspath : public Classpath { return thread; } + virtual object + makeJMethod(Thread* t, object vmMethod) + { + PROTECT(t, vmMethod); + + return byteArrayBody(t, methodName(t, vmMethod), 0) == '<' + ? makeJconstructor(t, vmMethod) + : makeJmethod(t, vmMethod); + } + + virtual object + getVMMethod(Thread* t, object jmethod) + { + return objectClass(t, jmethod) == type(t, Machine::JmethodType) + ? arrayBody + (t, classMethodTable + (t, jclassVmClass(t, jmethodClazz(t, jmethod))), + jmethodSlot(t, jmethod)) + : arrayBody + (t, classMethodTable + (t, jclassVmClass(t, jconstructorClazz(t, jmethod))), + jconstructorSlot(t, jmethod)); + } + + virtual object + makeJField(Thread* t, object vmField) + { + return makeJfield(t, vmField); + } + + virtual object + getVMField(Thread* t, object jfield) + { + return arrayBody + (t, classFieldTable + (t, jclassVmClass(t, jfieldClazz(t, jfield))), jfieldSlot(t, jfield)); + } + virtual void clearInterrupted(Thread* t) { @@ -520,15 +620,32 @@ class MyClasspath : public Classpath { virtual void runThread(Thread* t) { + // force monitor creation so we don't get an OutOfMemory error + // later when we try to acquire it: + objectMonitor(t, t->javaThread, true); + + THREAD_RESOURCE0(t, { + vm::acquire(t, t->javaThread); + t->flags &= ~Thread::ActiveFlag; + vm::notifyAll(t, t->javaThread); + vm::release(t, t->javaThread); + + object e = t->exception; + PROTECT(t, e); + + t->exception = 0; + + t->m->processor->invoke + (t, root(t, Machine::ThreadTerminated), + threadGroup(t, t->javaThread), t->javaThread); + + t->exception = e; + }); + object method = resolveMethod (t, root(t, Machine::BootLoader), "java/lang/Thread", "run", "()V"); t->m->processor->invoke(t, method, t->javaThread); - - acquire(t, t->javaThread); - t->flags &= ~Thread::ActiveFlag; - notifyAll(t, t->javaThread); - release(t, t->javaThread); } virtual void @@ -549,6 +666,20 @@ class MyClasspath : public Classpath { vm::resolveNative(t, method); } + virtual void + interceptMethods(Thread* t UNUSED) + { +#ifdef AVIAN_OPENJDK_SRC + interceptFileOperations(t, false); +#endif + } + + virtual void + preBoot(Thread*) + { + // ignore + } + virtual void boot(Thread* t) { @@ -557,11 +688,18 @@ class MyClasspath : public Classpath { resolveSystemClass(t, root(t, Machine::BootLoader), className(t, type(t, Machine::ClassLoaderType))); + setRoot(t, Machine::ThreadTerminated, resolveMethod + (t, root(t, Machine::BootLoader), "java/lang/ThreadGroup", + "threadTerminated", "(Ljava/lang/Thread;)V")); + #ifdef AVIAN_OPENJDK_SRC - interceptFileOperations(t); + interceptFileOperations(t, true); #else // not AVIAN_OPENJDK_SRC expect(t, loadLibrary(t, libraryPath, "verify", true, true)); expect(t, loadLibrary(t, libraryPath, "java", true, true)); +# ifndef PLATFORM_WINDOWS + loadLibrary(t, libraryPath, "mawt", true, true, false); +# endif #endif // not AVIAN_OPENJDK_SRC { object assertionLock = resolveField @@ -616,13 +754,20 @@ class MyClasspath : public Classpath { set(t, classStaticTable(t, type(t, Machine::ClassLoaderType)), fieldOffset(t, scl), root(t, Machine::AppLoader)); - cast(classStaticTable(t, type(t, Machine::ClassLoaderType)), + fieldAtOffset(classStaticTable(t, type(t, Machine::ClassLoaderType)), fieldOffset(t, sclSet)) = true; } t->m->processor->invoke (t, root(t, Machine::BootLoader), "java/lang/System", "initializeSystemClass", "()V", 0); + + t->m->processor->invoke + (t, root(t, Machine::BootLoader), "sun/misc/Launcher", + "getLauncher", "()Lsun/misc/Launcher;", 0); + + set(t, t->javaThread, ThreadContextClassLoader, + root(t, Machine::AppLoader)); } virtual const char* @@ -631,9 +776,146 @@ class MyClasspath : public Classpath { return classpath; } + virtual void + updatePackageMap(Thread* t, object class_) + { + PROTECT(t, class_); + + if (root(t, Machine::PackageMap) == 0) { + setRoot(t, Machine::PackageMap, makeHashMap(t, 0, 0)); + } + + object className = vm::className(t, class_); + if ('[' != byteArrayBody(t, className, 0)) { + THREAD_RUNTIME_ARRAY + (t, char, packageName, byteArrayLength(t, className)); + + char* s = reinterpret_cast(&byteArrayBody(t, className, 0)); + char* p = strrchr(s, '/'); + + if (p) { + int length = (p - s) + 1; + memcpy(RUNTIME_ARRAY_BODY(packageName), + &byteArrayBody(t, className, 0), + length); + RUNTIME_ARRAY_BODY(packageName)[length] = 0; + + object key = vm::makeByteArray + (t, "%s", RUNTIME_ARRAY_BODY(packageName)); + PROTECT(t, key); + + hashMapRemove + (t, root(t, Machine::PackageMap), key, byteArrayHash, + byteArrayEqual); + + object source = classSource(t, class_); + if (source) { + // note that we strip the "file:" prefix, since + // Package.defineSystemPackage expects an unadorned + // filename: + const unsigned PrefixLength = 5; + unsigned sourceNameLength = byteArrayLength(t, source) + - PrefixLength; + THREAD_RUNTIME_ARRAY(t, char, sourceName, sourceNameLength); + memcpy(RUNTIME_ARRAY_BODY(sourceName), + &byteArrayBody(t, source, PrefixLength), + sourceNameLength); + + source = vm::makeByteArray(t, "%s", RUNTIME_ARRAY_BODY(sourceName)); + } else { + source = vm::makeByteArray(t, "avian-dummy-package-source"); + } + + hashMapInsert + (t, root(t, Machine::PackageMap), key, source, byteArrayHash); + } + } + } + + virtual object + makeDirectByteBuffer(Thread* t, void* p, jlong capacity) + { + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/nio/DirectByteBuffer"); + PROTECT(t, c); + + object instance = makeNew(t, c); + PROTECT(t, instance); + + object constructor = resolveMethod(t, c, "", "(JI)V"); + + t->m->processor->invoke + (t, constructor, instance, reinterpret_cast(p), + static_cast(capacity)); + + return instance; + } + + virtual void* + getDirectBufferAddress(Thread* t, object b) + { + PROTECT(t, b); + + object field = resolveField(t, objectClass(t, b), "address", "J"); + + return reinterpret_cast + (fieldAtOffset(b, fieldOffset(t, field))); + } + + virtual int64_t + getDirectBufferCapacity(Thread* t, object b) + { + PROTECT(t, b); + + object field = resolveField + (t, objectClass(t, b), "capacity", "I"); + + return fieldAtOffset(b, fieldOffset(t, field)); + } + + virtual bool + canTailCall(Thread* t, object, object calleeClassName, + object calleeMethodName, object) + { + // we can't tail call System.loadLibrary or Runtime.loadLibrary + // due to their use of System.getCallerClass, which gets confused + // if we elide stack frames. + + return (strcmp("loadLibrary", reinterpret_cast + (&byteArrayBody(t, calleeMethodName, 0))) + or (strcmp("java/lang/System", reinterpret_cast + (&byteArrayBody(t, calleeClassName, 0))) + and strcmp("java/lang/Runtime", reinterpret_cast + (&byteArrayBody(t, calleeClassName, 0))))) + + // and we can't tail call Reflection.getCallerClass because the + // number of stack frames will be wrong + + and (strcmp("getCallerClass", reinterpret_cast + (&byteArrayBody(t, calleeMethodName, 0))) + or strcmp("sun/reflect/Reflection", reinterpret_cast + (&byteArrayBody(t, calleeClassName, 0)))); + } + + virtual void + shutDown(Thread* t) + { + object c = resolveClass + (t, root(t, Machine::BootLoader), "java/lang/Shutdown", false); + + if (c) { + object m = findMethodOrNull(t, c, "shutdown", "()V"); + + if (m) { + t->m->processor->invoke(t, m, 0); + } + } + } + virtual void dispose() { + allocator->free(buffer, bufferSize); allocator->free(this, sizeof(*this)); } @@ -643,6 +925,8 @@ class MyClasspath : public Classpath { const char* libraryPath; const char* tzMappings; const char* embedPrefix; + char* buffer; + unsigned bufferSize; unsigned tzMappingsLength; unsigned embedPrefixLength; unsigned filePathField; @@ -657,7 +941,6 @@ class MyClasspath : public Classpath { unsigned zipEntryMethodField; bool ranNetOnLoad; bool ranManagementOnLoad; - char buffer[BufferSize]; JmmInterface jmmInterface; }; @@ -681,49 +964,6 @@ struct jvm_version_info { unsigned: 32; }; -Finder* -getFinder(Thread* t, const char* name, unsigned nameLength) -{ - ACQUIRE(t, t->m->referenceLock); - - for (object p = root(t, Machine::VirtualFileFinders); - p; p = finderNext(t, p)) - { - if (byteArrayLength(t, finderName(t, p)) == nameLength - and strncmp(reinterpret_cast - (&byteArrayBody(t, finderName(t, p), 0)), - name, nameLength)) - { - return static_cast(finderFinder(t, p)); - } - } - - object n = makeByteArray(t, nameLength + 1); - memcpy(&byteArrayBody(t, n, 0), name, nameLength); - - void* p = t->m->libraries->resolve - (reinterpret_cast(&byteArrayBody(t, n, 0))); - - if (p) { - uint8_t* (*function)(unsigned*); - memcpy(&function, &p, BytesPerWord); - - unsigned size; - uint8_t* data = function(&size); - if (data) { - Finder* f = makeFinder(t->m->system, t->m->heap, data, size); - object finder = makeFinder - (t, f, n, root(t, Machine::VirtualFileFinders)); - - setRoot(t, Machine::VirtualFileFinders, finder); - - return f; - } - } - - return 0; -} - bool pathEqual(const char* a, const char* b, unsigned length) { @@ -793,7 +1033,7 @@ getFileAttributes MyClasspath* cp = static_cast(t->m->classpath); object file = reinterpret_cast(arguments[1]); - object path = cast(file, cp->filePathField); + object path = fieldAtOffset(file, cp->filePathField); THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1); stringChars(t, path, RUNTIME_ARRAY_BODY(p)); @@ -842,7 +1082,7 @@ checkFileAccess object file = reinterpret_cast(arguments[1]); unsigned mask = arguments[2]; - object path = cast(file, cp->filePathField); + object path = fieldAtOffset(file, cp->filePathField); THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1); stringChars(t, path, RUNTIME_ARRAY_BODY(p)); @@ -888,7 +1128,7 @@ getFileLength MyClasspath* cp = static_cast(t->m->classpath); object file = reinterpret_cast(arguments[1]); - object path = cast(file, cp->filePathField); + object path = fieldAtOffset(file, cp->filePathField); THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1); stringChars(t, path, RUNTIME_ARRAY_BODY(p)); @@ -974,8 +1214,8 @@ openFile(Thread* t, object method, uintptr_t* arguments) set(t, root(t, Machine::VirtualFiles), ArrayBody + (index * BytesPerWord), region); - cast - (cast + fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField) = index + VirtualFileBase; } else { @@ -993,8 +1233,8 @@ readByteFromFile(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int fd = cast - (cast + int fd = fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { @@ -1034,8 +1274,8 @@ readBytesFromFile(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int fd = cast - (cast + int fd = fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { @@ -1086,8 +1326,8 @@ skipBytesInFile(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int fd = cast - (cast + int fd = fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { @@ -1127,8 +1367,8 @@ availableBytesInFile(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int fd = cast - (cast + int fd = fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { @@ -1160,8 +1400,8 @@ closeFile(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int fd = cast - (cast + int fd = fieldAtOffset + (fieldAtOffset (this_, cp->fileInputStreamFdField), cp->fileDescriptorFdField); if (fd >= VirtualFileBase) { @@ -1371,7 +1611,8 @@ getZipFileEntry(Thread* t, object method, uintptr_t* arguments) RUNTIME_ARRAY_BODY(p)[byteArrayLength(t, path) + 1] = 0; } - return reinterpret_cast(find(file, p, byteArrayLength(t, path))); + return reinterpret_cast + (find(file, RUNTIME_ARRAY_BODY(p), byteArrayLength(t, path))); } else { int64_t entry = longValue (t, t->m->processor->invoke @@ -1466,15 +1707,15 @@ initializeZipEntryFields(Thread* t, object method, uintptr_t* arguments) set(t, this_, cp->zipEntryNameField, name); - cast(this_, cp->zipEntryTimeField) + fieldAtOffset(this_, cp->zipEntryTimeField) = fileTime(entry->start); - cast(this_, cp->zipEntryCrcField) + fieldAtOffset(this_, cp->zipEntryCrcField) = fileCRC(entry->start); - cast(this_, cp->zipEntrySizeField) + fieldAtOffset(this_, cp->zipEntrySizeField) = uncompressedSize(entry->start); - cast(this_, cp->zipEntryCsizeField) + fieldAtOffset(this_, cp->zipEntryCsizeField) = compressedSize(entry->start); - cast(this_, cp->zipEntryMethodField) + fieldAtOffset(this_, cp->zipEntryMethodField) = compressionMethod(entry->start); } else { t->m->processor->invoke @@ -1614,7 +1855,7 @@ getJarFileMetaInfEntryNames(Thread* t, object method, uintptr_t* arguments) MyClasspath* cp = static_cast(t->m->classpath); - int64_t peer = cast(this_, cp->zipFileJzfileField); + int64_t peer = fieldAtOffset(this_, cp->zipFileJzfileField); ZipFile* file = reinterpret_cast(peer); if (file->region) { return 0; @@ -1714,7 +1955,7 @@ loadLibrary(Thread* t, object, uintptr_t* arguments) #ifdef AVIAN_OPENJDK_SRC if (not absolute) { - if (strcmp(n, "net") == 0) { + if (strcmp(RUNTIME_ARRAY_BODY(n), "net") == 0) { bool ran; { ACQUIRE(t, t->m->classLock); @@ -1731,7 +1972,7 @@ loadLibrary(Thread* t, object, uintptr_t* arguments) } return; - } else if (strcmp(n, "management") == 0) { + } else if (strcmp(RUNTIME_ARRAY_BODY(n), "management") == 0) { bool ran; { ACQUIRE(t, t->m->classLock); @@ -1748,8 +1989,8 @@ loadLibrary(Thread* t, object, uintptr_t* arguments) } return; - } else if (strcmp(n, "zip") == 0 - or strcmp(n, "nio") == 0) + } else if (strcmp(RUNTIME_ARRAY_BODY(n), "zip") == 0 + or strcmp(RUNTIME_ARRAY_BODY(n), "nio") == 0) { return; } @@ -1761,46 +2002,8 @@ loadLibrary(Thread* t, object, uintptr_t* arguments) RUNTIME_ARRAY_BODY(n), not absolute, true); } -// only safe to call during bootstrap when there's only one thread -// running: void -intercept(Thread* t, object c, const char* name, const char* spec, - void* function) -{ - object m = findMethodOrNull(t, c, name, spec); - if (m) { - PROTECT(t, m); - - object clone = methodClone(t, m); - - // make clone private to prevent vtable updates at compilation - // time. Otherwise, our interception might be bypassed by calls - // through the vtable. - methodFlags(t, clone) |= ACC_PRIVATE; - - methodFlags(t, m) |= ACC_NATIVE; - - object native = makeNativeIntercept(t, function, true, clone); - - PROTECT(t, native); - - object runtimeData = getMethodRuntimeData(t, m); - - set(t, runtimeData, MethodRuntimeDataNative, native); - } else { - // If we can't find the method, just ignore it, since ProGuard may - // have stripped it out as unused. Otherwise, the code below can - // be uncommented for debugging purposes. - - // fprintf(stderr, "unable to find %s%s in %s\n", - // name, spec, &byteArrayBody(t, className(t, c), 0)); - - // abort(t); - } -} - -void -interceptFileOperations(Thread* t) +interceptFileOperations(Thread* t, bool updateRuntimeData) { MyClasspath* cp = static_cast(t->m->classpath); @@ -1843,22 +2046,22 @@ interceptFileOperations(Thread* t) cp->fileInputStreamFdField = fieldOffset(t, fileInputStreamFdField); intercept(t, fileInputStreamClass, "open", "(Ljava/lang/String;)V", - voidPointer(openFile)); + voidPointer(openFile), updateRuntimeData); intercept(t, fileInputStreamClass, "read", "()I", - voidPointer(readByteFromFile)); + voidPointer(readByteFromFile), updateRuntimeData); intercept(t, fileInputStreamClass, "readBytes", "([BII)I", - voidPointer(readBytesFromFile)); + voidPointer(readBytesFromFile), updateRuntimeData); intercept(t, fileInputStreamClass, "skip", "(J)J", - voidPointer(skipBytesInFile)); + voidPointer(skipBytesInFile), updateRuntimeData); intercept(t, fileInputStreamClass, "available", "()I", - voidPointer(availableBytesInFile)); + voidPointer(availableBytesInFile), updateRuntimeData); intercept(t, fileInputStreamClass, "close0", "()V", - voidPointer(closeFile)); + voidPointer(closeFile), updateRuntimeData); } } } @@ -1876,40 +2079,42 @@ interceptFileOperations(Thread* t) cp->zipFileJzfileField = fieldOffset(t, zipFileJzfileField); intercept(t, zipFileClass, "open", "(Ljava/lang/String;IJZ)J", - voidPointer(openZipFile)); + voidPointer(openZipFile), updateRuntimeData); intercept(t, zipFileClass, "getTotal", "(J)I", - voidPointer(getZipFileEntryCount)); + voidPointer(getZipFileEntryCount), updateRuntimeData); intercept(t, zipFileClass, "getEntry", "(J[BZ)J", - voidPointer(getZipFileEntry)); + voidPointer(getZipFileEntry), updateRuntimeData); intercept(t, zipFileClass, "getEntryBytes", "(JI)[B", - voidPointer(getZipFileEntryBytes)); + voidPointer(getZipFileEntryBytes), updateRuntimeData); intercept(t, zipFileClass, "getNextEntry", "(JI)J", - voidPointer(getNextZipFileEntry)); + voidPointer(getNextZipFileEntry), updateRuntimeData); intercept(t, zipFileClass, "getEntryMethod", "(J)I", - voidPointer(getZipFileEntryMethod)); + voidPointer(getZipFileEntryMethod), updateRuntimeData); intercept(t, zipFileClass, "freeEntry", "(JJ)V", - voidPointer(freeZipFileEntry)); + voidPointer(freeZipFileEntry), updateRuntimeData); intercept(t, zipFileClass, "read", "(JJJ[BII)I", - voidPointer(readZipFileEntry)); + voidPointer(readZipFileEntry), updateRuntimeData); intercept(t, zipFileClass, "getEntryCSize", "(J)J", - voidPointer(getZipFileEntryCompressedSize)); + voidPointer(getZipFileEntryCompressedSize), + updateRuntimeData); intercept(t, zipFileClass, "getEntrySize", "(J)J", - voidPointer(getZipFileEntryUncompressedSize)); + voidPointer(getZipFileEntryUncompressedSize), + updateRuntimeData); intercept(t, zipFileClass, "getZipMessage", "(J)Ljava/lang/String;", - voidPointer(getZipMessage)); + voidPointer(getZipMessage), updateRuntimeData); intercept(t, zipFileClass, "close", "(J)V", - voidPointer(closeZipFile)); + voidPointer(closeZipFile), updateRuntimeData); } } } @@ -1920,7 +2125,7 @@ interceptFileOperations(Thread* t) if (jarFileClass) { intercept(t, jarFileClass, "getMetaInfEntryNames", "()[Ljava/lang/String;", - voidPointer(getJarFileMetaInfEntryNames)); + voidPointer(getJarFileMetaInfEntryNames), updateRuntimeData); } } @@ -1940,27 +2145,27 @@ interceptFileOperations(Thread* t) PROTECT(t, fsClass); intercept(t, fsClass, gbaMethodName, "(Ljava/io/File;)I", - voidPointer(getFileAttributes)); + voidPointer(getFileAttributes), updateRuntimeData); intercept(t, fsClass, "checkAccess", "(Ljava/io/File;I)Z", - voidPointer(checkFileAccess)); + voidPointer(checkFileAccess), updateRuntimeData); intercept(t, fsClass, "getLength", "(Ljava/io/File;)J", - voidPointer(getFileLength)); + voidPointer(getFileLength), updateRuntimeData); } } intercept(t, type(t, Machine::ClassLoaderType), "loadLibrary", "(Ljava/lang/Class;Ljava/lang/String;Z)V", - voidPointer(loadLibrary)); + voidPointer(loadLibrary), updateRuntimeData); intercept(t, type(t, Machine::ClassLoaderType), "getBootstrapResource", "(Ljava/lang/String;)Ljava/net/URL;", - voidPointer(getBootstrapResource)); + voidPointer(getBootstrapResource), updateRuntimeData); intercept(t, type(t, Machine::ClassLoaderType), "getBootstrapResources", "(Ljava/lang/String;)Ljava/util/Enumeration;", - voidPointer(getBootstrapResources)); + voidPointer(getBootstrapResources), updateRuntimeData); } object @@ -2029,159 +2234,216 @@ countConstructors(Thread* t, object c, bool publicOnly) } object -resolveClassBySpec(Thread* t, object loader, const char* spec, - unsigned specLength) +makeJmethod(Thread* t, object vmMethod, int index) { - switch (*spec) { - case 'L': { - THREAD_RUNTIME_ARRAY(t, char, s, specLength - 1); - memcpy(RUNTIME_ARRAY_BODY(s), spec + 1, specLength - 2); - RUNTIME_ARRAY_BODY(s)[specLength - 2] = 0; - return resolveClass(t, loader, s); - } - - case '[': { - THREAD_RUNTIME_ARRAY(t, char, s, specLength + 1); - memcpy(RUNTIME_ARRAY_BODY(s), spec, specLength); - RUNTIME_ARRAY_BODY(s)[specLength] = 0; - return resolveClass(t, loader, s); + PROTECT(t, vmMethod); + + object name = intern + (t, t->m->classpath->makeString + (t, methodName(t, vmMethod), 0, byteArrayLength + (t, methodName(t, vmMethod)) - 1)); + PROTECT(t, name); + + unsigned parameterCount; + unsigned returnTypeSpec; + object parameterTypes = resolveParameterJTypes + (t, classLoader(t, methodClass(t, vmMethod)), methodSpec(t, vmMethod), + ¶meterCount, &returnTypeSpec); + PROTECT(t, parameterTypes); + + object returnType = resolveJType + (t, classLoader(t, methodClass(t, vmMethod)), reinterpret_cast + (&byteArrayBody(t, methodSpec(t, vmMethod), returnTypeSpec)), + byteArrayLength(t, methodSpec(t, vmMethod)) - 1 - returnTypeSpec); + PROTECT(t, returnType); + + object exceptionTypes = resolveExceptionJTypes + (t, classLoader(t, methodClass(t, vmMethod)), + methodAddendum(t, vmMethod)); + PROTECT(t, exceptionTypes); + + object signature; + object annotationTable; + object annotationDefault; + object addendum = methodAddendum(t, vmMethod); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + PROTECT(t, addendum); + + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + } + + annotationTable = addendumAnnotationTable(t, addendum); + + annotationDefault = methodAddendumAnnotationDefault(t, addendum); + } else { + signature = 0; + annotationTable = 0; + annotationDefault = 0; } - default: - return primitiveClass(t, *spec); + PROTECT(t, signature); + PROTECT(t, annotationTable); + PROTECT(t, annotationDefault); + + if (annotationTable or annotationDefault) { + object runtimeData = getClassRuntimeData(t, methodClass(t, vmMethod)); + + set(t, runtimeData, ClassRuntimeDataPool, + addendumPool(t, methodAddendum(t, vmMethod))); } -} -object -resolveJType(Thread* t, object loader, const char* spec, unsigned specLength) -{ - return getJClass(t, resolveClassBySpec(t, loader, spec, specLength)); -} - -object -resolveParameterTypes(Thread* t, object loader, object spec, - unsigned* parameterCount, unsigned* returnTypeSpec) -{ - PROTECT(t, loader); - PROTECT(t, spec); - - object list = 0; - PROTECT(t, list); - - unsigned offset = 1; - unsigned count = 0; - while (byteArrayBody(t, spec, offset) != ')') { - switch (byteArrayBody(t, spec, offset)) { - case 'L': { - unsigned start = offset; - ++ offset; - while (byteArrayBody(t, spec, offset) != ';') ++ offset; - ++ offset; - - object type = resolveClassBySpec - (t, loader, reinterpret_cast(&byteArrayBody(t, spec, start)), - offset - start); - - list = makePair(t, type, list); - - ++ count; - } break; - - case '[': { - unsigned start = offset; - while (byteArrayBody(t, spec, offset) == '[') ++ offset; - switch (byteArrayBody(t, spec, offset)) { - case 'L': - ++ offset; - while (byteArrayBody(t, spec, offset) != ';') ++ offset; - ++ offset; - break; - - default: - ++ offset; + if (index == -1) { + object table = classMethodTable(t, methodClass(t, vmMethod)); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (vmMethod == arrayBody(t, table, i)) { + index = i; break; } - - object type = resolveClassBySpec - (t, loader, reinterpret_cast(&byteArrayBody(t, spec, start)), - offset - start); - - list = makePair(t, type, list); - ++ count; - } break; - - default: - list = makePair - (t, primitiveClass(t, byteArrayBody(t, spec, offset)), list); - ++ offset; - ++ count; - break; } } - *parameterCount = count; - *returnTypeSpec = offset + 1; - return list; + expect(t, index != -1); + + object jclass = getJClass(t, methodClass(t, vmMethod)); + + return makeJmethod + (t, true, 0, jclass, index, name, returnType, parameterTypes, + exceptionTypes, methodFlags(t, vmMethod), signature, 0, annotationTable, + 0, annotationDefault, 0, 0, 0); } object -resolveParameterJTypes(Thread* t, object loader, object spec, - unsigned* parameterCount, unsigned* returnTypeSpec) +makeJconstructor(Thread* t, object vmMethod, int index) { - object list = resolveParameterTypes - (t, loader, spec, parameterCount, returnTypeSpec); + PROTECT(t, vmMethod); - PROTECT(t, list); - - object array = makeObjectArray - (t, type(t, Machine::JclassType), *parameterCount); - PROTECT(t, array); + unsigned parameterCount; + unsigned returnTypeSpec; + object parameterTypes = resolveParameterJTypes + (t, classLoader(t, methodClass(t, vmMethod)), methodSpec(t, vmMethod), + ¶meterCount, &returnTypeSpec); + PROTECT(t, parameterTypes); - for (int i = *parameterCount - 1; i >= 0; --i) { - object c = getJClass(t, pairFirst(t, list)); - set(t, array, ArrayBody + (i * BytesPerWord), c); - list = pairSecond(t, list); - } + object exceptionTypes = resolveExceptionJTypes + (t, classLoader(t, methodClass(t, vmMethod)), + methodAddendum(t, vmMethod)); + PROTECT(t, exceptionTypes); - return array; -} + object signature; + object annotationTable; + object addendum = methodAddendum(t, vmMethod); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + PROTECT(t, addendum); -object -resolveExceptionJTypes(Thread* t, object loader, object addendum) -{ - if (addendum == 0 or methodAddendumExceptionTable(t, addendum) == 0) { - return makeObjectArray(t, type(t, Machine::JclassType), 0); - } - - PROTECT(t, loader); - PROTECT(t, addendum); - - object array = makeObjectArray - (t, type(t, Machine::JclassType), - shortArrayLength(t, methodAddendumExceptionTable(t, addendum))); - PROTECT(t, array); - - for (unsigned i = 0; i < shortArrayLength - (t, methodAddendumExceptionTable(t, addendum)); ++i) - { - uint16_t index = shortArrayBody - (t, methodAddendumExceptionTable(t, addendum), i) - 1; - - object o = singletonObject(t, addendumPool(t, addendum), index); - - if (objectClass(t, o) == type(t, Machine::ReferenceType)) { - o = resolveClass(t, loader, referenceName(t, o)); - - set(t, addendumPool(t, addendum), SingletonBody + (index * BytesPerWord), - o); + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); } - o = getJClass(t, o); - - set(t, array, ArrayBody + (i * BytesPerWord), o); + annotationTable = addendumAnnotationTable(t, addendum); + } else { + signature = 0; + annotationTable = 0; } - return array; + PROTECT(t, signature); + PROTECT(t, annotationTable); + + if (annotationTable) { + object runtimeData = getClassRuntimeData(t, methodClass(t, vmMethod)); + + set(t, runtimeData, ClassRuntimeDataPool, + addendumPool(t, methodAddendum(t, vmMethod))); + } + + if (index == -1) { + object table = classMethodTable(t, methodClass(t, vmMethod)); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (vmMethod == arrayBody(t, table, i)) { + index = i; + break; + } + } + } + + expect(t, index != -1); + + object jclass = getJClass(t, methodClass(t, vmMethod)); + + return makeJconstructor + (t, true, 0, jclass, index, parameterTypes, exceptionTypes, methodFlags + (t, vmMethod), signature, 0, annotationTable, 0, 0, 0, 0); +} + +object +makeJfield(Thread* t, object vmField, int index) +{ + PROTECT(t, vmField); + + object name = intern + (t, t->m->classpath->makeString + (t, fieldName(t, vmField), 0, byteArrayLength + (t, fieldName(t, vmField)) - 1)); + PROTECT(t, name); + + object type = resolveClassBySpec + (t, classLoader(t, fieldClass(t, vmField)), + reinterpret_cast + (&byteArrayBody(t, fieldSpec(t, vmField), 0)), + byteArrayLength(t, fieldSpec(t, vmField)) - 1); + PROTECT(t, type); + + type = getJClass(t, type); + + object signature; + object annotationTable; + object addendum = fieldAddendum(t, vmField); + if (addendum) { + signature = addendumSignature(t, addendum); + if (signature) { + PROTECT(t, addendum); + + signature = t->m->classpath->makeString + (t, signature, 0, byteArrayLength(t, signature) - 1); + } + + annotationTable = addendumAnnotationTable(t, addendum); + } else { + signature = 0; + annotationTable = 0; + } + + PROTECT(t, signature); + PROTECT(t, annotationTable); + + if (annotationTable) { + object runtimeData = getClassRuntimeData(t, fieldClass(t, vmField)); + + set(t, runtimeData, ClassRuntimeDataPool, + addendumPool(t, fieldAddendum(t, vmField))); + } + + if (index == -1) { + object table = classFieldTable(t, fieldClass(t, vmField)); + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + if (vmField == arrayBody(t, table, i)) { + index = i; + break; + } + } + } + + expect(t, index != -1); + + object jclass = getJClass(t, fieldClass(t, vmField)); + + return makeJfield + (t, true, 0, jclass, index, name, type, fieldFlags + (t, vmField), signature, 0, annotationTable, 0, 0, 0, 0); } void @@ -2285,11 +2547,28 @@ fieldForOffsetInClass(Thread* t, object c, unsigned offset) object fieldForOffset(Thread* t, object o, unsigned offset) { - object field = fieldForOffsetInClass(t, objectClass(t, o), offset); - if (field) { - return field; - } else { + object c = objectClass(t, o); + if (classVmFlags(t, c) & SingletonFlag) { + c = singletonObject(t, o, 0); + object table = classFieldTable(t, c); + if (table) { + for (unsigned i = 0; i < objectArrayLength(t, table); ++i) { + object field = objectArrayBody(t, table, i); + if ((fieldFlags(t, field) & ACC_STATIC) + and fieldOffset(t, field) == offset) + { + return field; + } + } + } abort(t); + } else { + object field = fieldForOffsetInClass(t, c, offset); + if (field) { + return field; + } else { + abort(t); + } } } @@ -2313,10 +2592,13 @@ extern "C" JNIEXPORT int64_t JNICALL Avian_java_lang_Class_getSuperclass (Thread* t, object, uintptr_t* arguments) { - object super = classSuper - (t, jclassVmClass(t, reinterpret_cast(arguments[0]))); - - return super ? reinterpret_cast(getJClass(t, super)) : 0; + object class_ = jclassVmClass(t, reinterpret_cast(arguments[0])); + if (classFlags(t, class_) & ACC_INTERFACE) { + return 0; + } else { + object super = classSuper(t, class_); + return super ? reinterpret_cast(getJClass(t, super)) : 0; + } } extern "C" JNIEXPORT void @@ -2449,7 +2731,7 @@ Avian_sun_misc_Unsafe_getObject object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT void JNICALL @@ -2470,7 +2752,7 @@ Avian_sun_misc_Unsafe_getShort__Ljava_lang_Object_2J object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT int64_t JNICALL @@ -2480,7 +2762,7 @@ Avian_sun_misc_Unsafe_getInt__Ljava_lang_Object_2J object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT int64_t JNICALL @@ -2490,17 +2772,21 @@ Avian_sun_misc_Unsafe_getFloat__Ljava_lang_Object_2J object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getIntVolatile -(Thread*, object, uintptr_t* arguments) +(Thread* t, object, uintptr_t* arguments) { object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - int32_t result = cast(o, offset); + // avoid blocking the VM if this is being called in a busy loop + PROTECT(t, o); + { ENTER(t, Thread::IdleState); } + + int32_t result = fieldAtOffset(o, offset); loadMemoryBarrier(); return result; } @@ -2512,7 +2798,7 @@ Avian_sun_misc_Unsafe_getLong__Ljava_lang_Object_2J object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT int64_t JNICALL @@ -2530,6 +2816,10 @@ Avian_sun_misc_Unsafe_getLongVolatile object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); + // avoid blocking the VM if this is being called in a busy loop + PROTECT(t, o); + { ENTER(t, Thread::IdleState); } + object field; if (BytesPerWord < 8) { field = local::fieldForOffset(t, o, offset); @@ -2538,7 +2828,7 @@ Avian_sun_misc_Unsafe_getLongVolatile acquire(t, field); } - int64_t result = cast(o, offset); + int64_t result = fieldAtOffset(o, offset); if (BytesPerWord < 8) { release(t, field); @@ -2557,7 +2847,7 @@ Avian_sun_misc_Unsafe_putByte__Ljava_lang_Object_2JB int64_t offset; memcpy(&offset, arguments + 2, 8); int8_t value = arguments[4]; - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT void JNICALL @@ -2568,7 +2858,7 @@ Avian_sun_misc_Unsafe_putShort__Ljava_lang_Object_2JS int64_t offset; memcpy(&offset, arguments + 2, 8); int16_t value = arguments[4]; - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT void JNICALL @@ -2579,7 +2869,7 @@ Avian_sun_misc_Unsafe_putInt__Ljava_lang_Object_2JI int64_t offset; memcpy(&offset, arguments + 2, 8); int32_t value = arguments[4]; - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT void JNICALL @@ -2590,7 +2880,7 @@ Avian_sun_misc_Unsafe_putFloat__Ljava_lang_Object_2JF int64_t offset; memcpy(&offset, arguments + 2, 8); int32_t value = arguments[4]; - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT int64_t JNICALL @@ -2600,7 +2890,7 @@ Avian_sun_misc_Unsafe_getByte__Ljava_lang_Object_2J object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); - return cast(o, offset); + return fieldAtOffset(o, offset); } extern "C" JNIEXPORT int64_t JNICALL @@ -2619,7 +2909,7 @@ Avian_sun_misc_Unsafe_putBoolean__Ljava_lang_Object_2JZ int64_t offset; memcpy(&offset, arguments + 2, 8); uint8_t value = arguments[4]; - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT void JNICALL @@ -2630,17 +2920,21 @@ Avian_sun_misc_Unsafe_putLong__Ljava_lang_Object_2JJ int64_t offset; memcpy(&offset, arguments + 2, 8); int64_t value; memcpy(&value, arguments + 4, 8); - cast(o, offset) = value; + fieldAtOffset(o, offset) = value; } extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_getObjectVolatile -(Thread*, object, uintptr_t* arguments) +(Thread* t, object, uintptr_t* arguments) { object o = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); + + // avoid blocking the VM if this is being called in a busy loop + PROTECT(t, o); + { ENTER(t, Thread::IdleState); } - uintptr_t value = cast(o, offset); + uintptr_t value = fieldAtOffset(o, offset); loadMemoryBarrier(); return value; } @@ -2665,19 +2959,6 @@ Avian_sun_misc_Unsafe_putOrderedObject Avian_sun_misc_Unsafe_putObjectVolatile(t, method, arguments); } -extern "C" JNIEXPORT int64_t JNICALL -Avian_sun_misc_Unsafe_compareAndSwapInt -(Thread*, object, uintptr_t* arguments) -{ - object target = reinterpret_cast(arguments[1]); - int64_t offset; memcpy(&offset, arguments + 2, 8); - uint32_t expect = arguments[4]; - uint32_t update = arguments[5]; - - return atomicCompareAndSwap32 - (&cast(target, offset), expect, update); -} - extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_compareAndSwapObject (Thread* t, object, uintptr_t* arguments) @@ -2688,7 +2969,7 @@ Avian_sun_misc_Unsafe_compareAndSwapObject uintptr_t update = arguments[5]; bool success = atomicCompareAndSwap - (&cast(target, offset), expect, update); + (&fieldAtOffset(target, offset), expect, update); if (success) { mark(t, target, offset); @@ -2708,11 +2989,11 @@ Avian_sun_misc_Unsafe_compareAndSwapLong #ifdef AVIAN_HAS_CAS64 return atomicCompareAndSwap64 - (&cast(target, offset), expect, update); + (&fieldAtOffset(target, offset), expect, update); #else ACQUIRE_FIELD_FOR_WRITE(t, local::fieldForOffset(t, target, offset)); - if (cast(target, offset) == expect) { - cast(target, offset) = update; + if (fieldAtOffset(target, offset) == expect) { + fieldAtOffset(target, offset) = update; return true; } else { return false; @@ -2999,6 +3280,12 @@ jvmInitProperties(Thread* t, uintptr_t* arguments) "Avian Contributors"); local::setProperty(t, method, *properties, "java.vm.name","Avian"); +#ifdef AVIAN_VERSION + local::setProperty(t, method, *properties, "java.vm.version",AVIAN_VERSION); +#endif +#ifdef AVIAN_INFO + local::setProperty(t, method, *properties, "java.vm.info",AVIAN_INFO); +#endif local::setProperty (t, method, *properties, "java.home", @@ -3013,7 +3300,7 @@ jvmInitProperties(Thread* t, uintptr_t* arguments) static_cast (systemClassLoaderFinder(t, root(t, Machine::BootLoader)))->path()); - local::setProperty(t, method, *properties, "file.encoding", "ASCII"); + local::setProperty(t, method, *properties, "file.encoding", "UTF-8"); #ifdef ARCH_x86_32 local::setProperty(t, method, *properties, "os.arch", "x86"); #elif defined ARCH_x86_64 @@ -3033,8 +3320,8 @@ jvmInitProperties(Thread* t, uintptr_t* arguments) if (*p == '=') { THREAD_RUNTIME_ARRAY(t, char, name, (p - start) + 1); - memcpy(name, start, p - start); - name[p - start] = 0; + memcpy(RUNTIME_ARRAY_BODY(name), start, p - start); + RUNTIME_ARRAY_BODY(name)[p - start] = 0; local::setProperty (t, method, *properties, RUNTIME_ARRAY_BODY(name), p + 1); } @@ -3109,7 +3396,7 @@ EXPORT(JVM_FreeMemory)() extern "C" JNIEXPORT jlong JNICALL EXPORT(JVM_MaxMemory)() { - return 0; + return local::globalMachine->heap->limit(); } extern "C" JNIEXPORT jint JNICALL @@ -3501,10 +3788,37 @@ EXPORT(JVM_ClassDepth)(Thread*, jstring) { abort(); } extern "C" JNIEXPORT jint JNICALL EXPORT(JVM_ClassLoaderDepth)(Thread*) { abort(); } -extern "C" JNIEXPORT jstring JNICALL -EXPORT(JVM_GetSystemPackage)(Thread*, jstring s) +uint64_t +jvmGetSystemPackage(Thread* t, uintptr_t* arguments) { - return s; + jstring s = reinterpret_cast(arguments[0]); + + ACQUIRE(t, t->m->classLock); + + THREAD_RUNTIME_ARRAY(t, char, chars, stringLength(t, *s) + 1); + stringChars(t, *s, RUNTIME_ARRAY_BODY(chars)); + + object key = makeByteArray(t, RUNTIME_ARRAY_BODY(chars)); + + object array = hashMapFind + (t, root(t, Machine::PackageMap), key, byteArrayHash, byteArrayEqual); + + if (array) { + return reinterpret_cast + (makeLocalReference + (t, t->m->classpath->makeString + (t, array, 0, byteArrayLength(t, array)))); + } else { + return 0; + } +} + +extern "C" JNIEXPORT jstring JNICALL +EXPORT(JVM_GetSystemPackage)(Thread* t, jstring s) +{ + uintptr_t arguments[] = { reinterpret_cast(s) }; + + return reinterpret_cast(run(t, jvmGetSystemPackage, arguments)); } uint64_t @@ -3576,7 +3890,7 @@ EXPORT(JVM_GetArrayLength)(Thread* t, jobject array) { ENTER(t, Thread::ActiveState); - return cast(*array, BytesPerWord); + return fieldAtOffset(*array, BytesPerWord); } uint64_t @@ -3589,40 +3903,40 @@ jvmGetArrayElement(Thread* t, uintptr_t* arguments) case 'Z': return reinterpret_cast (makeLocalReference - (t, makeBoolean(t, cast(*array, ArrayBody + index)))); + (t, makeBoolean(t, fieldAtOffset(*array, ArrayBody + index)))); case 'B': return reinterpret_cast (makeLocalReference - (t, makeByte(t, cast(*array, ArrayBody + index)))); + (t, makeByte(t, fieldAtOffset(*array, ArrayBody + index)))); case 'C': return reinterpret_cast (makeLocalReference - (t, makeChar(t, cast(*array, ArrayBody + (index * 2))))); + (t, makeChar(t, fieldAtOffset(*array, ArrayBody + (index * 2))))); case 'S': return reinterpret_cast (makeLocalReference - (t, makeShort(t, cast(*array, ArrayBody + (index * 2))))); + (t, makeShort(t, fieldAtOffset(*array, ArrayBody + (index * 2))))); case 'I': return reinterpret_cast (makeLocalReference - (t, makeInt(t, cast(*array, ArrayBody + (index * 4))))); + (t, makeInt(t, fieldAtOffset(*array, ArrayBody + (index * 4))))); case 'F': return reinterpret_cast (makeLocalReference - (t, makeFloat(t, cast(*array, ArrayBody + (index * 4))))); + (t, makeFloat(t, fieldAtOffset(*array, ArrayBody + (index * 4))))); case 'J': return reinterpret_cast (makeLocalReference - (t, makeLong(t, cast(*array, ArrayBody + (index * 8))))); + (t, makeLong(t, fieldAtOffset(*array, ArrayBody + (index * 8))))); case 'D': return reinterpret_cast (makeLocalReference - (t, makeDouble(t, cast(*array, ArrayBody + (index * 8))))); + (t, makeDouble(t, fieldAtOffset(*array, ArrayBody + (index * 8))))); case 'L': case '[': return reinterpret_cast (makeLocalReference - (t, cast(*array, ArrayBody + (index * BytesPerWord)))); + (t, fieldAtOffset(*array, ArrayBody + (index * BytesPerWord)))); default: abort(t); } @@ -3648,28 +3962,28 @@ EXPORT(JVM_SetArrayElement)(Thread* t, jobject array, jint index, switch (byteArrayBody(t, className(t, objectClass(t, *array)), 1)) { case 'Z': - cast(*array, ArrayBody + index) = booleanValue(t, *value); + fieldAtOffset(*array, ArrayBody + index) = booleanValue(t, *value); break; case 'B': - cast(*array, ArrayBody + index) = byteValue(t, *value); + fieldAtOffset(*array, ArrayBody + index) = byteValue(t, *value); break; case 'C': - cast(*array, ArrayBody + (index * 2)) = charValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 2)) = charValue(t, *value); break; case 'S': - cast(*array, ArrayBody + (index * 2)) = shortValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 2)) = shortValue(t, *value); break; case 'I': - cast(*array, ArrayBody + (index * 4)) = intValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 4)) = intValue(t, *value); break; case 'F': - cast(*array, ArrayBody + (index * 4)) = floatValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 4)) = floatValue(t, *value); break; case 'J': - cast(*array, ArrayBody + (index * 8)) = longValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 8)) = longValue(t, *value); break; case 'D': - cast(*array, ArrayBody + (index * 8)) = doubleValue(t, *value); + fieldAtOffset(*array, ArrayBody + (index * 8)) = doubleValue(t, *value); break; case 'L': case '[': @@ -3772,7 +4086,7 @@ EXPORT(JVM_GetCallerClass)(Thread* t, int target) { ENTER(t, Thread::ActiveState); - object method = getCaller(t, target); + object method = getCaller(t, target, true); return method ? makeLocalReference (t, getJClass(t, methodClass(t, method))) : 0; @@ -4134,7 +4448,10 @@ jvmGetDeclaredClasses(Thread* t, uintptr_t* arguments) unsigned count = 0; for (unsigned i = 0; i < arrayLength(t, table); ++i) { - if (innerClassReferenceOuter(t, arrayBody(t, table, i))) { + object outer = innerClassReferenceOuter(t, arrayBody(t, table, i)); + if (outer and byteArrayEqual + (t, outer, className(t, jclassVmClass(t, *c)))) + { ++ count; } } @@ -4143,11 +4460,14 @@ jvmGetDeclaredClasses(Thread* t, uintptr_t* arguments) PROTECT(t, result); for (unsigned i = 0; i < arrayLength(t, table); ++i) { - if (innerClassReferenceOuter(t, arrayBody(t, table, i))) { + object outer = innerClassReferenceOuter(t, arrayBody(t, table, i)); + if (outer and byteArrayEqual + (t, outer, className(t, jclassVmClass(t, *c)))) + { object inner = getJClass (t, resolveClass - (t, classLoader(t, jclassVmClass(t, *c)), referenceName - (t, innerClassReferenceInner(t, arrayBody(t, table, i))))); + (t, classLoader(t, jclassVmClass(t, *c)), + innerClassReferenceInner(t, arrayBody(t, table, i)))); -- count; set(t, result, ArrayBody + (count * BytesPerWord), inner); @@ -4175,13 +4495,29 @@ jvmGetDeclaringClass(Thread* t, uintptr_t* arguments) { jclass c = reinterpret_cast(arguments[0]); - object method = resolveMethod - (t, root(t, Machine::BootLoader), "avian/OpenJDK", "getDeclaringClass", - "(Lavian/VMClass;)Ljava/lang/Class;"); + object class_ = jclassVmClass(t, *c); + object addendum = classAddendum(t, class_); + if (addendum) { + object table = classAddendumInnerClassTable(t, addendum); + if (table) { + for (unsigned i = 0; i < arrayLength(t, table); ++i) { + object reference = arrayBody(t, table, i); + if (strcmp + (&byteArrayBody(t, innerClassReferenceInner(t, reference), 0), + &byteArrayBody(t, className(t, class_), 0)) == 0) + { + return reinterpret_cast + (makeLocalReference + (t, getJClass + (t, resolveClass + (t, classLoader(t, class_), innerClassReferenceOuter + (t, reference))))); + } + } + } + } - return reinterpret_cast - (makeLocalReference - (t, t->m->processor->invoke(t, method, 0, jclassVmClass(t, *c)))); + return 0; } extern "C" JNIEXPORT jclass JNICALL @@ -4251,65 +4587,7 @@ jvmGetClassDeclaredMethods(Thread* t, uintptr_t* arguments) if (((not publicOnly) or (methodFlags(t, vmMethod) & ACC_PUBLIC)) and byteArrayBody(t, methodName(t, vmMethod), 0) != '<') { - object name = intern - (t, t->m->classpath->makeString - (t, methodName(t, vmMethod), 0, byteArrayLength - (t, methodName(t, vmMethod)) - 1)); - PROTECT(t, name); - - unsigned parameterCount; - unsigned returnTypeSpec; - object parameterTypes = local::resolveParameterJTypes - (t, classLoader(t, jclassVmClass(t, *c)), methodSpec(t, vmMethod), - ¶meterCount, &returnTypeSpec); - PROTECT(t, parameterTypes); - - object returnType = local::resolveJType - (t, classLoader(t, jclassVmClass(t, *c)), reinterpret_cast - (&byteArrayBody(t, methodSpec(t, vmMethod), returnTypeSpec)), - byteArrayLength(t, methodSpec(t, vmMethod)) - 1 - returnTypeSpec); - PROTECT(t, returnType); - - object exceptionTypes = local::resolveExceptionJTypes - (t, classLoader(t, jclassVmClass(t, *c)), - methodAddendum(t, vmMethod)); - PROTECT(t, exceptionTypes); - - object signature; - object annotationTable; - object annotationDefault; - object addendum = methodAddendum(t, vmMethod); - if (addendum) { - signature = addendumSignature(t, addendum); - if (signature) { - signature = t->m->classpath->makeString - (t, signature, 0, byteArrayLength(t, signature) - 1); - } - - annotationTable = addendumAnnotationTable(t, addendum); - - annotationDefault = methodAddendumAnnotationDefault(t, addendum); - } else { - signature = 0; - annotationTable = 0; - annotationDefault = 0; - } - - if (annotationTable or annotationDefault) { - PROTECT(t, signature); - PROTECT(t, annotationTable); - PROTECT(t, annotationDefault); - - object runtimeData = getClassRuntimeData(t, jclassVmClass(t, *c)); - - set(t, runtimeData, ClassRuntimeDataPool, - addendumPool(t, methodAddendum(t, vmMethod))); - } - - object method = makeJmethod - (t, true, 0, *c, i, name, returnType, parameterTypes, exceptionTypes, - methodFlags(t, vmMethod), signature, 0, annotationTable, 0, - annotationDefault, 0, 0, 0); + object method = makeJmethod(t, vmMethod, i); assert(t, ai < objectArrayLength(t, array)); @@ -4354,50 +4632,7 @@ jvmGetClassDeclaredFields(Thread* t, uintptr_t* arguments) PROTECT(t, vmField); if ((not publicOnly) or (fieldFlags(t, vmField) & ACC_PUBLIC)) { - object name = intern - (t, t->m->classpath->makeString - (t, fieldName(t, vmField), 0, byteArrayLength - (t, fieldName(t, vmField)) - 1)); - PROTECT(t, name); - - object type = local::resolveClassBySpec - (t, classLoader(t, jclassVmClass(t, *c)), - reinterpret_cast - (&byteArrayBody(t, fieldSpec(t, vmField), 0)), - byteArrayLength(t, fieldSpec(t, vmField)) - 1); - PROTECT(t, type); - - type = getJClass(t, type); - - object signature; - object annotationTable; - object addendum = fieldAddendum(t, vmField); - if (addendum) { - signature = addendumSignature(t, addendum); - if (signature) { - signature = t->m->classpath->makeString - (t, signature, 0, byteArrayLength(t, signature) - 1); - } - - annotationTable = addendumAnnotationTable(t, addendum); - } else { - signature = 0; - annotationTable = 0; - } - - if (annotationTable) { - PROTECT(t, signature); - PROTECT(t, annotationTable); - - object runtimeData = getClassRuntimeData(t, jclassVmClass(t, *c)); - - set(t, runtimeData, ClassRuntimeDataPool, - addendumPool(t, fieldAddendum(t, vmField))); - } - - object field = makeJfield - (t, true, 0, *c, i, name, type, fieldFlags - (t, vmField), signature, 0, annotationTable, 0, 0, 0, 0); + object field = makeJfield(t, vmField, i); assert(t, ai < objectArrayLength(t, array)); @@ -4448,47 +4683,7 @@ jvmGetClassDeclaredConstructors(Thread* t, uintptr_t* arguments) (&byteArrayBody(t, methodName(t, vmMethod), 0)), "") == 0) { - unsigned parameterCount; - unsigned returnTypeSpec; - object parameterTypes = local::resolveParameterJTypes - (t, classLoader(t, jclassVmClass(t, *c)), methodSpec(t, vmMethod), - ¶meterCount, &returnTypeSpec); - PROTECT(t, parameterTypes); - - object exceptionTypes = local::resolveExceptionJTypes - (t, classLoader(t, jclassVmClass(t, *c)), - methodAddendum(t, vmMethod)); - PROTECT(t, exceptionTypes); - - object signature; - object annotationTable; - object addendum = methodAddendum(t, vmMethod); - if (addendum) { - signature = addendumSignature(t, addendum); - if (signature) { - signature = t->m->classpath->makeString - (t, signature, 0, byteArrayLength(t, signature) - 1); - } - - annotationTable = addendumAnnotationTable(t, addendum); - } else { - signature = 0; - annotationTable = 0; - } - - if (annotationTable) { - PROTECT(t, signature); - PROTECT(t, annotationTable); - - object runtimeData = getClassRuntimeData(t, jclassVmClass(t, *c)); - - set(t, runtimeData, ClassRuntimeDataPool, - addendumPool(t, methodAddendum(t, vmMethod))); - } - - object method = makeJconstructor - (t, true, 0, *c, i, parameterTypes, exceptionTypes, methodFlags - (t, vmMethod), signature, 0, annotationTable, 0, 0, 0, 0); + object method = makeJconstructor(t, vmMethod, i); assert(t, ai < objectArrayLength(t, array)); @@ -4536,18 +4731,14 @@ jvmInvokeMethod(Thread* t, uintptr_t* arguments) instance = 0; } - unsigned returnCode = methodReturnCode(t, vmMethod); - - object result; - if (args) { - result = t->m->processor->invokeArray - (t, vmMethod, instance ? *instance : 0, *args); - } else { - result = t->m->processor->invoke(t, vmMethod, instance ? *instance : 0); + if (instance and not instanceOf(t, methodClass(t, vmMethod), *instance)) { + throwNew(t, Machine::IllegalArgumentExceptionType); } return reinterpret_cast - (makeLocalReference(t, translateInvokeResult(t, returnCode, result))); + (makeLocalReference + (t, invoke + (t, vmMethod, instance ? *instance : 0, args ? *args : 0))); } extern "C" JNIEXPORT jobject JNICALL @@ -4576,6 +4767,14 @@ jvmNewInstanceFromConstructor(Thread* t, uintptr_t* arguments) (t, jclassVmClass(t, jconstructorClazz(t, *constructor))), jconstructorSlot(t, *constructor)); + THREAD_RESOURCE0(t, { + if (t->exception) { + object exception = t->exception; + t->exception = makeThrowable + (t, Machine::InvocationTargetExceptionType, 0, 0, exception); + } + }); + if (args) { t->m->processor->invokeArray(t, method, instance, *args); } else { @@ -4664,16 +4863,36 @@ EXPORT(JVM_ConstantPoolGetIntAt)(Thread* t, jobject, jobject pool, jint index) } extern "C" JNIEXPORT jlong JNICALL -EXPORT(JVM_ConstantPoolGetLongAt)(Thread*, jobject, jobject, jint) -{ abort(); } +EXPORT(JVM_ConstantPoolGetLongAt)(Thread* t, jobject, jobject pool, jint index) +{ + ENTER(t, Thread::ActiveState); + + uint64_t v; + memcpy(&v, &singletonValue(t, *pool, index - 1), 8); + + return v; +} extern "C" JNIEXPORT jfloat JNICALL -EXPORT(JVM_ConstantPoolGetFloatAt)(Thread*, jobject, jobject, jint) -{ abort(); } +EXPORT(JVM_ConstantPoolGetFloatAt)(Thread* t, jobject, jobject pool, + jint index) +{ + ENTER(t, Thread::ActiveState); + + return bitsToFloat(singletonValue(t, *pool, index - 1)); +} extern "C" JNIEXPORT jdouble JNICALL -EXPORT(JVM_ConstantPoolGetDoubleAt)(Thread*, jobject, jobject, jint) -{ abort(); } +EXPORT(JVM_ConstantPoolGetDoubleAt)(Thread* t, jobject, jobject pool, + jint index) +{ + ENTER(t, Thread::ActiveState); + + double v; + memcpy(&v, &singletonValue(t, *pool, index - 1), 8); + + return v; +} extern "C" JNIEXPORT jstring JNICALL EXPORT(JVM_ConstantPoolGetStringAt)(Thread*, jobject, jobject, jint) @@ -4685,12 +4904,12 @@ jvmConstantPoolGetUTF8At(Thread* t, uintptr_t* arguments) jobject pool = reinterpret_cast(arguments[0]); jint index = arguments[1]; - object array = singletonObject(t, *pool, index - 1); + object array = parseUtf8(t, singletonObject(t, *pool, index - 1)); return reinterpret_cast (makeLocalReference (t, t->m->classpath->makeString - (t, array, 0, cast(array, BytesPerWord) - 1))); + (t, array, 0, fieldAtOffset(array, BytesPerWord) - 1))); } extern "C" JNIEXPORT jstring JNICALL @@ -5334,13 +5553,61 @@ EXPORT(JVM_GetManagement)(jint version) extern "C" JNIEXPORT jobject JNICALL EXPORT(JVM_InitAgentProperties)(Thread*, jobject) { abort(); } -extern "C" JNIEXPORT jobjectArray JNICALL -EXPORT(JVM_GetEnclosingMethodInfo)(JNIEnv*, jclass) +uint64_t +getEnclosingMethodInfo(Thread* t, uintptr_t* arguments) { - // todo: implement properly + jclass c = reinterpret_cast(arguments[0]); + object class_ = jclassVmClass(t, *c); + PROTECT(t, class_); + + object addendum = classAddendum(t, class_); + if (addendum) { + object enclosingClass = classAddendumEnclosingClass(t, addendum); + if (enclosingClass) { + PROTECT(t, enclosingClass); + + object array = makeObjectArray(t, type(t, Machine::JobjectType), 3); + PROTECT(t, array); + + enclosingClass = getJClass + (t, resolveClass(t, classLoader(t, class_), enclosingClass)); + + set(t, array, ArrayBody, enclosingClass); + + object enclosingMethod = classAddendumEnclosingMethod(t, addendum); + + if (enclosingMethod) { + PROTECT(t, enclosingMethod); + + object name = t->m->classpath->makeString + (t, pairFirst(t, enclosingMethod), 0, + byteArrayLength(t, pairFirst(t, enclosingMethod)) - 1); + + set(t, array, ArrayBody + BytesPerWord, name); + + object spec = t->m->classpath->makeString + (t, pairSecond(t, enclosingMethod), 0, + byteArrayLength(t, pairSecond(t, enclosingMethod)) - 1); + + set(t, array, ArrayBody + (2 * BytesPerWord), spec); + } + + return reinterpret_cast(makeLocalReference(t, array)); + } + } return 0; } + +extern "C" JNIEXPORT jobjectArray JNICALL +EXPORT(JVM_GetEnclosingMethodInfo)(Thread* t, jclass c) +{ + uintptr_t arguments[] = { reinterpret_cast(c) }; + + return reinterpret_cast + (run(t, getEnclosingMethodInfo, arguments)); +} + extern "C" JNIEXPORT jintArray JNICALL EXPORT(JVM_GetThreadStateValues)(JNIEnv*, jint) { abort(); } @@ -5433,7 +5700,7 @@ Avian_java_util_TimeZone_getSystemTimeZoneID return 0; } - Finder* finder = local::getFinder(t, ef.jar, ef.jarLength); + Finder* finder = getFinder(t, ef.jar, ef.jarLength); if (finder == 0) { return 0; } diff --git a/src/codegen/compiler.cpp b/src/codegen/compiler.cpp new file mode 100644 index 0000000000..784022c3b5 --- /dev/null +++ b/src/codegen/compiler.cpp @@ -0,0 +1,3063 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include + +#include +#include +#include +#include + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/read.h" +#include "codegen/compiler/event.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/ir.h" + +using namespace vm; + +namespace avian { +namespace codegen { +namespace compiler { + +const bool DebugAppend = false; +const bool DebugCompile = false; +const bool DebugResources = false; +const bool DebugFrame = false; +const bool DebugControl = false; +const bool DebugBuddies = false; + +const unsigned StealRegisterReserveCount = 2; + +// this should be equal to the largest number of registers used by a +// compare instruction: +const unsigned ResolveRegisterReserveCount = (TargetBytesPerWord == 8 ? 2 : 4); + +void +apply(Context* c, lir::UnaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High); + +void +apply(Context* c, lir::BinaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High); + +void +apply(Context* c, lir::TernaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High, + unsigned s3Size, Site* s3Low, Site* s3High); + + +class ConstantPoolNode { + public: + ConstantPoolNode(Promise* promise): promise(promise), next(0) { } + + Promise* promise; + ConstantPoolNode* next; +}; + +Read* +live(Context* c UNUSED, Value* v) +{ + assert(c, v->buddy->hasBuddy(c, v)); + + Value* p = v; + do { + if (valid(p->reads)) { + return p->reads; + } + p = p->buddy; + } while (p != v); + + return 0; +} + +unsigned +sitesToString(Context* c, Value* v, char* buffer, unsigned size); + +void +deadWord(Context* c, Value* v) +{ + Value* nextWord = v->nextWord; + assert(c, nextWord != v); + + for (SiteIterator it(c, v, true, false); it.hasMore();) { + Site* s = it.next(); + + if (s->registerSize(c) > TargetBytesPerWord) { + it.remove(c); + nextWord->addSite(c, s); + } + } +} + +void +deadBuddy(Context* c, Value* v, Read* r UNUSED) +{ + assert(c, v->buddy != v); + assert(c, r); + + if (DebugBuddies) { + fprintf(stderr, "remove dead buddy %p from", v); + for (Value* p = v->buddy; p != v; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } + + assert(c, v->buddy); + + Value* next = v->buddy; + v->buddy = v; + Value* p = next; + while (p->buddy != v) p = p->buddy; + p->buddy = next; + + assert(c, p->buddy); + + for (SiteIterator it(c, v, false, false); it.hasMore();) { + Site* s = it.next(); + it.remove(c); + + next->addSite(c, s); + } +} + +void +popRead(Context* c, Event* e UNUSED, Value* v) +{ + assert(c, e == v->reads->event); + + if (DebugReads) { + fprintf(stderr, "pop read %p from %p next %p event %p (%s)\n", + v->reads, v, v->reads->next(c), e, (e ? e->name() : 0)); + } + + v->reads = v->reads->next(c); + + if (not valid(v->reads)) { + Value* nextWord = v->nextWord; + if (nextWord != v) { + if (valid(nextWord->reads)) { + deadWord(c, v); + } else { + deadWord(c, nextWord); + } + } + + Read* r = live(c, v); + if (r) { + deadBuddy(c, v, r); + } else { + v->clearSites(c); + } + } +} + +void +addBuddy(Value* original, Value* buddy) +{ + buddy->buddy = original; + Value* p = original; + while (p->buddy != original) p = p->buddy; + p->buddy = buddy; + + if (DebugBuddies) { + fprintf(stderr, "add buddy %p to", buddy); + for (Value* p = buddy->buddy; p != buddy; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } +} + +lir::ValueType +valueType(Context* c, Compiler::OperandType type) +{ + switch (type) { + case Compiler::ObjectType: + case Compiler::AddressType: + case Compiler::IntegerType: + case Compiler::VoidType: + return lir::ValueGeneral; + case Compiler::FloatType: + return lir::ValueFloat; + default: + abort(c); + } +} + +void +move(Context* c, Value* value, Site* src, Site* dst); + +unsigned +sitesToString(Context* c, Site* sites, char* buffer, unsigned size) +{ + unsigned total = 0; + for (Site* s = sites; s; s = s->next) { + total += s->toString(c, buffer + total, size - total); + + if (s->next) { + assert(c, size > total + 2); + memcpy(buffer + total, ", ", 2); + total += 2; + } + } + + assert(c, size > total); + buffer[total] = 0; + + return total; +} + +unsigned +sitesToString(Context* c, Value* v, char* buffer, unsigned size) +{ + unsigned total = 0; + Value* p = v; + do { + if (total) { + assert(c, size > total + 2); + memcpy(buffer + total, "; ", 2); + total += 2; + } + + if (p->sites) { + total += vm::snprintf(buffer + total, size - total, "%p has ", p); + total += sitesToString(c, p->sites, buffer + total, size - total); + } else { + total += vm::snprintf(buffer + total, size - total, "%p has nothing", p); + } + + p = p->buddy; + } while (p != v); + + return total; +} + +Site* +pickTargetSite(Context* c, Read* read, bool intersectRead = false, + unsigned registerReserveCount = 0, + CostCalculator* costCalculator = 0) +{ + Target target + (pickTarget + (c, read, intersectRead, registerReserveCount, costCalculator)); + + expect(c, target.cost < Target::Impossible); + + if (target.type == lir::MemoryOperand) { + return frameSite(c, target.index); + } else { + return registerSite(c, target.index); + } +} + +bool +acceptMatch(Context* c, Site* s, Read*, const SiteMask& mask) +{ + return s->match(c, mask); +} + +Site* +pickSourceSite(Context* c, Read* read, Site* target = 0, + unsigned* cost = 0, SiteMask* extraMask = 0, + bool intersectRead = true, bool includeBuddies = true, + bool includeNextWord = true, + bool (*accept)(Context*, Site*, Read*, const SiteMask&) + = acceptMatch) +{ + SiteMask mask; + + if (extraMask) { + mask = mask.intersectionWith(*extraMask); + } + + if (intersectRead) { + read->intersect(&mask); + } + + Site* site = 0; + unsigned copyCost = 0xFFFFFFFF; + for (SiteIterator it(c, read->value, includeBuddies, includeNextWord); + it.hasMore();) + { + Site* s = it.next(); + if (accept(c, s, read, mask)) { + unsigned v = s->copyCost(c, target); + if (v < copyCost) { + site = s; + copyCost = v; + } + } + } + + if (DebugMoves and site and target) { + char srcb[256]; site->toString(c, srcb, 256); + char dstb[256]; target->toString(c, dstb, 256); + fprintf(stderr, "pick source %s to %s for %p cost %d\n", + srcb, dstb, read->value, copyCost); + } + + if (cost) *cost = copyCost; + return site; +} + +Site* +maybeMove(Context* c, Read* read, bool intersectRead, bool includeNextWord, + unsigned registerReserveCount = 0) +{ + Value* value = read->value; + unsigned size = value == value->nextWord ? TargetBytesPerWord : 8; + + class MyCostCalculator: public CostCalculator { + public: + MyCostCalculator(Value* value, unsigned size, bool includeNextWord): + value(value), + size(size), + includeNextWord(includeNextWord) + { } + + virtual unsigned cost(Context* c, SiteMask dstMask) + { + OperandMask src; + OperandMask tmp; + c->arch->planMove + (size, src, tmp, + OperandMask(dstMask.typeMask, dstMask.registerMask)); + + SiteMask srcMask = SiteMask::lowPart(src); + for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { + Site* s = it.next(); + if (s->match(c, srcMask) or s->match(c, dstMask)) { + return 0; + } + } + + return Target::IndirectMovePenalty; + } + + Value* value; + unsigned size; + bool includeNextWord; + } costCalculator(value, size, includeNextWord); + + Site* dstSite = pickTargetSite + (c, read, intersectRead, registerReserveCount, &costCalculator); + + OperandMask src; + OperandMask tmp; + c->arch->planMove + (size, src, tmp, + OperandMask(1 << dstSite->type(c), dstSite->registerMask(c))); + + SiteMask srcMask = SiteMask::lowPart(src); + unsigned cost = 0xFFFFFFFF; + Site* srcSite = 0; + for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { + Site* s = it.next(); + unsigned v = s->copyCost(c, dstSite); + if (v == 0) { + srcSite = s; + cost = 0; + break; + } + if (not s->match(c, srcMask)) { + v += CopyPenalty; + } + if (v < cost) { + srcSite = s; + cost = v; + } + } + + if (cost) { + if (DebugMoves) { + char srcb[256]; srcSite->toString(c, srcb, 256); + char dstb[256]; dstSite->toString(c, dstb, 256); + fprintf(stderr, "maybe move %s to %s for %p to %p\n", + srcb, dstb, value, value); + } + + srcSite->freeze(c, value); + + value->addSite(c, dstSite); + + srcSite->thaw(c, value); + + if (not srcSite->match(c, srcMask)) { + srcSite->freeze(c, value); + dstSite->freeze(c, value); + + SiteMask tmpMask = SiteMask::lowPart(tmp); + SingleRead tmpRead(tmpMask, 0); + tmpRead.value = value; + tmpRead.successor_ = value; + + Site* tmpSite = pickTargetSite(c, &tmpRead, true); + + value->addSite(c, tmpSite); + + move(c, value, srcSite, tmpSite); + + dstSite->thaw(c, value); + srcSite->thaw(c, value); + + srcSite = tmpSite; + } + + move(c, value, srcSite, dstSite); + } + + return dstSite; +} + +Site* +maybeMove(Context* c, Value* v, const SiteMask& mask, bool intersectMask, + bool includeNextWord, unsigned registerReserveCount = 0) +{ + SingleRead read(mask, 0); + read.value = v; + read.successor_ = v; + + return maybeMove + (c, &read, intersectMask, includeNextWord, registerReserveCount); +} + +Site* +pickSiteOrMove(Context* c, Read* read, bool intersectRead, + bool includeNextWord, unsigned registerReserveCount = 0) +{ + Site* s = pickSourceSite + (c, read, 0, 0, 0, intersectRead, true, includeNextWord); + + if (s) { + return s; + } else { + return maybeMove + (c, read, intersectRead, includeNextWord, registerReserveCount); + } +} + +Site* +pickSiteOrMove(Context* c, Value* v, const SiteMask& mask, bool intersectMask, + bool includeNextWord, unsigned registerReserveCount = 0) +{ + SingleRead read(mask, 0); + read.value = v; + read.successor_ = v; + + return pickSiteOrMove + (c, &read, intersectMask, includeNextWord, registerReserveCount); +} + +void +steal(Context* c, Resource* r, Value* thief) +{ + if (DebugResources) { + char resourceBuffer[256]; r->toString(c, resourceBuffer, 256); + char siteBuffer[1024]; sitesToString(c, r->value, siteBuffer, 1024); + fprintf(stderr, "%p steal %s from %p (%s)\n", + thief, resourceBuffer, r->value, siteBuffer); + } + + if ((not (thief and thief->isBuddyOf(r->value)) + and r->value->uniqueSite(c, r->site))) + { + r->site->freeze(c, r->value); + + maybeMove(c, live(c, r->value), false, true, StealRegisterReserveCount); + + r->site->thaw(c, r->value); + } + + r->value->removeSite(c, r->site); +} + +SiteMask +generalRegisterMask(Context* c) +{ + return SiteMask + (1 << lir::RegisterOperand, c->regFile->generalRegisters.mask, NoFrameIndex); +} + +SiteMask +generalRegisterOrConstantMask(Context* c) +{ + return SiteMask + ((1 << lir::RegisterOperand) | (1 << lir::ConstantOperand), + c->regFile->generalRegisters.mask, NoFrameIndex); +} + +MultiRead* +multiRead(Context* c) +{ + return new(c->zone) MultiRead; +} + +StubRead* +stubRead(Context* c) +{ + return new(c->zone) StubRead; +} + +Site* +pickSite(Context* c, Value* v, Site* s, unsigned index, bool includeNextWord) +{ + for (SiteIterator it(c, v, true, includeNextWord); it.hasMore();) { + Site* candidate = it.next(); + if (s->matchNextWord(c, candidate, index)) { + return candidate; + } + } + + return 0; +} + +Site* +pickSiteOrMove(Context* c, Value* v, Site* s, unsigned index) +{ + Site* n = pickSite(c, v, s, index, false); + if (n) { + return n; + } + + return maybeMove(c, v, s->nextWordMask(c, index), true, false); +} + +Site* +pickSiteOrMove(Context* c, Value* v, Site* s, Site** low, Site** high) +{ + if (v->wordIndex == 0) { + *low = s; + *high = pickSiteOrMove(c, v->nextWord, s, 1); + return *high; + } else { + *low = pickSiteOrMove(c, v->nextWord, s, 0); + *high = s; + return *low; + } +} + +Site* +pickSiteOrGrow(Context* c, Value* v, Site* s, unsigned index) +{ + Site* n = pickSite(c, v, s, index, false); + if (n) { + return n; + } + + n = s->makeNextWord(c, index); + v->addSite(c, n); + return n; +} + +Site* +pickSiteOrGrow(Context* c, Value* v, Site* s, Site** low, Site** high) +{ + if (v->wordIndex == 0) { + *low = s; + *high = pickSiteOrGrow(c, v->nextWord, s, 1); + return *high; + } else { + *low = pickSiteOrGrow(c, v->nextWord, s, 0); + *high = s; + return *low; + } +} + +bool +isHome(Value* v, int frameIndex) +{ + Value* p = v; + do { + if (p->home == frameIndex) { + return true; + } + p = p->buddy; + } while (p != v); + + return false; +} + +bool +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) == lir::RegisterOperand) { + return c->availableGeneralRegisterCount > ResolveRegisterReserveCount; + } else { + assert(c, s->match(c, SiteMask(1 << lir::MemoryOperand, 0, AnyFrameIndex))); + + return isHome(read->value, offsetToFrameIndex + (c, static_cast(s)->offset)); + } + } else { + return false; + } +} + +void +move(Context* c, Value* value, Site* src, Site* dst) +{ + if (DebugMoves) { + char srcb[256]; src->toString(c, srcb, 256); + char dstb[256]; dst->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p to %p\n", + srcb, dstb, value, value); + } + + assert(c, value->findSite(dst)); + + src->freeze(c, value); + dst->freeze(c, value); + + unsigned srcSize; + unsigned dstSize; + if (value->nextWord == value) { + srcSize = TargetBytesPerWord; + dstSize = TargetBytesPerWord; + } else { + srcSize = src->registerSize(c); + dstSize = dst->registerSize(c); + } + + if (srcSize == dstSize) { + apply(c, lir::Move, srcSize, src, src, dstSize, dst, dst); + } else if (srcSize > TargetBytesPerWord) { + Site* low, *high, *other = pickSiteOrGrow(c, value, dst, &low, &high); + other->freeze(c, value->nextWord); + + apply(c, lir::Move, srcSize, src, src, srcSize, low, high); + + other->thaw(c, value->nextWord); + } else { + Site* low, *high, *other = pickSiteOrMove(c, value, src, &low, &high); + other->freeze(c, value->nextWord); + + apply(c, lir::Move, dstSize, low, high, dstSize, dst, dst); + + other->thaw(c, value->nextWord); + } + + dst->thaw(c, value); + src->thaw(c, value); +} + +void +asAssemblerOperand(Context* c, Site* low, Site* high, + lir::Operand* result) +{ + low->asAssemblerOperand(c, high, result); +} + +class OperandUnion: public lir::Operand { + // must be large enough and aligned properly to hold any operand + // type (we'd use an actual union type here, except that classes + // with constructors cannot be used in a union): + uintptr_t padding[4]; +}; + +void +apply(Context* c, lir::UnaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High) +{ + assert(c, s1Low->type(c) == s1High->type(c)); + + lir::OperandType s1Type = s1Low->type(c); + OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); + + c->assembler->apply(op, + OperandInfo(s1Size, s1Type, &s1Union)); +} + +void +apply(Context* c, lir::BinaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High) +{ + assert(c, s1Low->type(c) == s1High->type(c)); + assert(c, s2Low->type(c) == s2High->type(c)); + + lir::OperandType s1Type = s1Low->type(c); + OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); + + lir::OperandType s2Type = s2Low->type(c); + OperandUnion s2Union; asAssemblerOperand(c, s2Low, s2High, &s2Union); + + c->assembler->apply(op, + OperandInfo(s1Size, s1Type, &s1Union), + OperandInfo(s2Size, s2Type, &s2Union)); +} + +void +apply(Context* c, lir::TernaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High, + unsigned s3Size, Site* s3Low, Site* s3High) +{ + assert(c, s1Low->type(c) == s1High->type(c)); + assert(c, s2Low->type(c) == s2High->type(c)); + assert(c, s3Low->type(c) == s3High->type(c)); + + lir::OperandType s1Type = s1Low->type(c); + OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); + + lir::OperandType s2Type = s2Low->type(c); + OperandUnion s2Union; asAssemblerOperand(c, s2Low, s2High, &s2Union); + + lir::OperandType s3Type = s3Low->type(c); + OperandUnion s3Union; asAssemblerOperand(c, s3Low, s3High, &s3Union); + + c->assembler->apply(op, + OperandInfo(s1Size, s1Type, &s1Union), + OperandInfo(s2Size, s2Type, &s2Union), + OperandInfo(s3Size, s3Type, &s3Union)); +} + +void +append(Context* c, Event* e); + +void +saveLocals(Context* c, Event* e) +{ + for (unsigned li = 0; li < c->localFootprint; ++li) { + Local* local = e->localsBefore + li; + if (local->value) { + if (DebugReads) { + fprintf(stderr, "local save read %p at %d of %d\n", + local->value, compiler::frameIndex(c, li), totalFrameSize(c)); + } + + e->addRead(c, local->value, SiteMask + (1 << lir::MemoryOperand, 0, compiler::frameIndex(c, li))); + } + } +} + +void +maybeMove(Context* c, lir::BinaryOperation type, unsigned srcSize, + unsigned srcSelectSize, Value* srcValue, unsigned dstSize, Value* dstValue, + const SiteMask& dstMask) +{ + Read* read = live(c, dstValue); + bool isStore = read == 0; + + Site* target; + if (dstValue->target) { + target = dstValue->target; + } else if (isStore) { + return; + } else { + target = pickTargetSite(c, read); + } + + unsigned cost = srcValue->source->copyCost(c, target); + + if (srcSelectSize < dstSize) cost = 1; + + if (cost) { + // todo: let c->arch->planMove decide this: + bool useTemporary = ((target->type(c) == lir::MemoryOperand + and srcValue->source->type(c) == lir::MemoryOperand) + or (srcSelectSize < dstSize + and target->type(c) != lir::RegisterOperand)); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, target); + + srcValue->source->thaw(c, srcValue); + + bool addOffset = srcSize != srcSelectSize + and c->arch->bigEndian() + and srcValue->source->type(c) == lir::MemoryOperand; + + if (addOffset) { + static_cast(srcValue->source)->offset + += (srcSize - srcSelectSize); + } + + target->freeze(c, dstValue); + + if (target->match(c, dstMask) and not useTemporary) { + if (DebugMoves) { + char srcb[256]; srcValue->source->toString(c, srcb, 256); + char dstb[256]; target->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p to %p\n", + srcb, dstb, srcValue, dstValue); + } + + srcValue->source->freeze(c, srcValue); + + apply(c, type, min(srcSelectSize, dstSize), srcValue->source, srcValue->source, + dstSize, target, target); + + srcValue->source->thaw(c, srcValue); + } else { + // pick a temporary register which is valid as both a + // destination and a source for the moves we need to perform: + + dstValue->removeSite(c, target); + + bool thunk; + OperandMask src; + + c->arch->planSource(type, dstSize, src, dstSize, &thunk); + + if (srcValue->type == lir::ValueGeneral) { + src.registerMask &= c->regFile->generalRegisters.mask; + } + + assert(c, thunk == 0); + assert(c, dstMask.typeMask & src.typeMask & (1 << lir::RegisterOperand)); + + Site* tmpTarget = freeRegisterSite + (c, dstMask.registerMask & src.registerMask); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, tmpTarget); + + tmpTarget->freeze(c, dstValue); + + if (DebugMoves) { + char srcb[256]; srcValue->source->toString(c, srcb, 256); + char dstb[256]; tmpTarget->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p to %p\n", + srcb, dstb, srcValue, dstValue); + } + + apply(c, type, srcSelectSize, srcValue->source, srcValue->source, + dstSize, tmpTarget, tmpTarget); + + tmpTarget->thaw(c, dstValue); + + srcValue->source->thaw(c, srcValue); + + if (useTemporary or isStore) { + if (DebugMoves) { + char srcb[256]; tmpTarget->toString(c, srcb, 256); + char dstb[256]; target->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p to %p\n", + srcb, dstb, srcValue, dstValue); + } + + dstValue->addSite(c, target); + + tmpTarget->freeze(c, dstValue); + + apply(c, lir::Move, dstSize, tmpTarget, tmpTarget, dstSize, target, target); + + tmpTarget->thaw(c, dstValue); + + if (isStore) { + dstValue->removeSite(c, tmpTarget); + } + } + } + + target->thaw(c, dstValue); + + if (addOffset) { + static_cast(srcValue->source)->offset + -= (srcSize - srcSelectSize); + } + } else { + target = srcValue->source; + + if (DebugMoves) { + char dstb[256]; target->toString(c, dstb, 256); + fprintf(stderr, "null move in %s for %p to %p\n", dstb, srcValue, dstValue); + } + } + + if (isStore) { + dstValue->removeSite(c, target); + } +} + +Site* +pickMatchOrMove(Context* c, Read* r, Site* nextWord, unsigned index, + bool intersectRead) +{ + Site* s = pickSite(c, r->value, nextWord, index, true); + SiteMask mask; + if (intersectRead) { + r->intersect(&mask); + } + if (s and s->match(c, mask)) { + return s; + } + + return pickSiteOrMove + (c, r->value, mask.intersectionWith(nextWord->nextWordMask(c, index)), + true, true); +} + +Site* +pickSiteOrMove(Context* c, Value* src, Value* dst, Site* nextWord, + unsigned index) +{ + if (live(c, dst)) { + Read* read = live(c, src); + Site* s; + if (nextWord) { + s = pickMatchOrMove(c, read, nextWord, index, false); + } else { + s = pickSourceSite(c, read, 0, 0, 0, false, true, true); + + if (s == 0 or s->isVolatile(c)) { + s = maybeMove(c, read, false, true); + } + } + assert(c, s); + + addBuddy(src, dst); + + if (src->source->isVolatile(c)) { + src->removeSite(c, src->source); + } + + return s; + } else { + return 0; + } +} + +ConstantSite* +findConstantSite(Context* c, Value* v); + +Site* +getTarget(Context* c, Value* value, Value* result, const SiteMask& resultMask); + +void +freezeSource(Context* c, unsigned size, Value* v); + +void +thawSource(Context* c, unsigned size, Value* v); + +void +removeBuddy(Context* c, Value* v) +{ + if (v->buddy != v) { + if (DebugBuddies) { + fprintf(stderr, "remove buddy %p from", v); + for (Value* p = v->buddy; p != v; p = p->buddy) { + fprintf(stderr, " %p", p); + } + fprintf(stderr, "\n"); + } + + assert(c, v->buddy); + + Value* next = v->buddy; + v->buddy = v; + Value* p = next; + while (p->buddy != v) p = p->buddy; + p->buddy = next; + + assert(c, p->buddy); + + if (not live(c, next)) { + next->clearSites(c); + } + + if (not live(c, v)) { + v->clearSites(c); + } + } +} + +Site* +copy(Context* c, Site* s) +{ + Site* start = 0; + Site* end = 0; + for (; s; s = s->next) { + Site* n = s->copy(c); + if (end) { + end->next = n; + } else { + start = n; + } + end = n; + } + return start; +} + +class Snapshot { + public: + Snapshot(Context* c, Value* value, Snapshot* next): + value(value), buddy(value->buddy), sites(copy(c, value->sites)), next(next) + { } + + Value* value; + Value* buddy; + Site* sites; + Snapshot* next; +}; + +Snapshot* +snapshot(Context* c, Value* value, Snapshot* next) +{ + if (DebugControl) { + char buffer[256]; sitesToString(c, value->sites, buffer, 256); + fprintf(stderr, "snapshot %p buddy %p sites %s\n", + value, value->buddy, buffer); + } + + return new(c->zone) Snapshot(c, value, next); +} + +Snapshot* +makeSnapshots(Context* c, Value* value, Snapshot* next) +{ + next = snapshot(c, value, next); + for (Value* p = value->buddy; p != value; p = p->buddy) { + next = snapshot(c, p, next); + } + return next; +} + +Value* +maybeBuddy(Context* c, Value* v); + +Value* +pushWord(Context* c, Value* v) +{ + if (v) { + v = maybeBuddy(c, v); + } + + Stack* s = stack(c, v, c->stack); + + if (DebugFrame) { + fprintf(stderr, "push %p\n", v); + } + + if (v) { + v->home = frameIndex(c, s->index + c->localFootprint); + } + c->stack = s; + + return v; +} + +void +push(Context* c, unsigned footprint, Value* v) +{ + assert(c, footprint); + + bool bigEndian = c->arch->bigEndian(); + + Value* low = v; + + if (bigEndian) { + v = pushWord(c, v); + } + + Value* high; + if (footprint > 1) { + assert(c, footprint == 2); + + if (TargetBytesPerWord == 4) { + low->maybeSplit(c); + high = pushWord(c, low->nextWord); + } else { + high = pushWord(c, 0); + } + } else { + high = 0; + } + + if (not bigEndian) { + v = pushWord(c, v); + } + + if (high) { + v->nextWord = high; + high->nextWord = v; + high->wordIndex = 1; + } +} + +void +popWord(Context* c) +{ + Stack* s = c->stack; + assert(c, s->value == 0 or s->value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "pop %p\n", s->value); + } + + c->stack = s->next; +} + +Value* +pop(Context* c, unsigned footprint) +{ + assert(c, footprint); + + Stack* s = 0; + + bool bigEndian = c->arch->bigEndian(); + + if (not bigEndian) { + s = c->stack; + } + + if (footprint > 1) { + assert(c, footprint == 2); + +#ifndef NDEBUG + Stack* low; + Stack* high; + if (bigEndian) { + high = c->stack; + low = high->next; + } else { + low = c->stack; + high = low->next; + } + + assert(c, (TargetBytesPerWord == 8 + and low->value->nextWord == low->value and high->value == 0) + or (TargetBytesPerWord == 4 and low->value->nextWord == high->value)); +#endif // not NDEBUG + + popWord(c); + } + + if (bigEndian) { + s = c->stack; + } + + popWord(c); + + return s->value; +} + +Value* +storeLocal(Context* c, unsigned footprint, Value* v, unsigned index, bool copy) +{ + assert(c, index + footprint <= c->localFootprint); + + if (copy) { + unsigned sizeInBytes = sizeof(Local) * c->localFootprint; + Local* newLocals = static_cast(c->zone->allocate(sizeInBytes)); + memcpy(newLocals, c->locals, sizeInBytes); + c->locals = newLocals; + } + + Value* high; + if (footprint > 1) { + assert(c, footprint == 2); + + unsigned highIndex; + unsigned lowIndex; + if (c->arch->bigEndian()) { + highIndex = index + 1; + lowIndex = index; + } else { + lowIndex = index + 1; + highIndex = index; + } + + if (TargetBytesPerWord == 4) { + assert(c, v->nextWord != v); + + high = storeLocal(c, 1, v->nextWord, highIndex, false); + } else { + high = 0; + } + + index = lowIndex; + } else { + high = 0; + } + + v = maybeBuddy(c, v); + + if (high != 0) { + v->nextWord = high; + high->nextWord = v; + high->wordIndex = 1; + } + + Local* local = c->locals + index; + local->value = v; + + if (DebugFrame) { + fprintf(stderr, "store local %p at %d\n", local->value, index); + } + + local->value->home = frameIndex(c, index); + + return v; +} + +Value* +loadLocal(Context* c, unsigned footprint, unsigned index) +{ + assert(c, index + footprint <= c->localFootprint); + + if (footprint > 1) { + assert(c, footprint == 2); + + if (not c->arch->bigEndian()) { + ++ index; + } + } + + assert(c, c->locals[index].value); + assert(c, c->locals[index].value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "load local %p at %d\n", c->locals[index].value, index); + } + + return c->locals[index].value; +} + +Value* +register_(Context* c, int number) +{ + assert(c, (1 << number) & (c->regFile->generalRegisters.mask + | c->regFile->floatRegisters.mask)); + + Site* s = registerSite(c, number); + lir::ValueType type = ((1 << number) & c->regFile->floatRegisters.mask) + ? lir::ValueFloat: lir::ValueGeneral; + + return value(c, type, s, s); +} + +unsigned +frameFootprint(Context* c, Stack* s) +{ + return c->localFootprint + (s ? (s->index + 1) : 0); +} + +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); + + ForkState* forkState = link->forkState; + if (forkState) { + for (unsigned i = 0; i < forkState->readCount; ++i) { + 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); + if (not live(c, v)) { + v->clearSites(c); + } + } + } + + JunctionState* junctionState = link->junctionState; + if (junctionState) { + for (unsigned i = 0; i < junctionState->frameFootprint; ++i) { + StubReadPair* p = junctionState->reads + i; + + if (p->value and p->value->reads) { + assert(c, p->value->reads == p->read); + popRead(c, 0, p->value); + } + } + } +} + +class BuddyEvent: public Event { + public: + BuddyEvent(Context* c, Value* original, Value* buddy): + Event(c), original(original), buddy(buddy) + { + this->addRead(c, original, SiteMask(~0, ~0, AnyFrameIndex), buddy); + } + + virtual const char* name() { + return "BuddyEvent"; + } + + virtual void compile(Context* c) { + if (DebugBuddies) { + fprintf(stderr, "original %p buddy %p\n", original, buddy); + } + + assert(c, original->hasSite(c)); + + assert(c, original); + assert(c, buddy); + + addBuddy(original, buddy); + + popRead(c, this, original); + } + + Value* original; + Value* buddy; +}; + +void +appendBuddy(Context* c, Value* original, Value* buddy) +{ + append(c, new(c->zone) BuddyEvent(c, original, buddy)); +} + +void +append(Context* c, Event* e) +{ + LogicalInstruction* i = c->logicalCode[c->logicalIp]; + if (c->stack != i->stack or c->locals != i->locals) { + appendDummy(c); + } + + if (DebugAppend) { + fprintf(stderr, " -- append %s at %d with %d stack before\n", + e->name(), e->logicalInstruction->index, c->stack ? + c->stack->index + 1 : 0); + } + + if (c->lastEvent) { + c->lastEvent->next = e; + } else { + c->firstEvent = e; + } + c->lastEvent = e; + + Event* p = c->predecessor; + if (p) { + if (DebugAppend) { + fprintf(stderr, "%d precedes %d\n", p->logicalInstruction->index, + e->logicalInstruction->index); + } + + Link* link = compiler::link + (c, p, e->predecessors, e, p->successors, c->forkState); + e->predecessors = link; + p->successors = link; + } + c->forkState = 0; + + c->predecessor = e; + + if (e->logicalInstruction->firstEvent == 0) { + e->logicalInstruction->firstEvent = e; + } + e->logicalInstruction->lastEvent = e; +} + +Site* +readSource(Context* c, Read* r) +{ + Value* v = r->value; + + if (DebugReads) { + char buffer[1024]; sitesToString(c, v, buffer, 1024); + fprintf(stderr, "read source for %p from %s\n", v, buffer); + } + + if (not v->hasSite(c)) { + if (DebugReads) { + fprintf(stderr, "no sites found for %p\n", v); + } + return 0; + } + + Value* high = r->high(c); + if (high) { + return pickMatchOrMove(c, r, high->source, 0, true); + } else { + return pickSiteOrMove(c, r, true, true); + } +} + +void +propagateJunctionSites(Context* c, Event* e, Site** sites) +{ + for (Link* pl = e->predecessors; pl; pl = pl->nextPredecessor) { + Event* p = pl->predecessor; + if (p->junctionSites == 0) { + p->junctionSites = sites; + for (Link* sl = p->successors; sl; sl = sl->nextSuccessor) { + Event* s = sl->successor; + propagateJunctionSites(c, s, sites); + } + } + } +} + +void +propagateJunctionSites(Context* c, Event* e) +{ + for (Link* sl = e->successors; sl; sl = sl->nextSuccessor) { + Event* s = sl->successor; + if (s->predecessors->nextPredecessor) { + unsigned size = sizeof(Site*) * frameFootprint(c, e->stackAfter); + Site** junctionSites = static_cast + (c->zone->allocate(size)); + memset(junctionSites, 0, size); + + propagateJunctionSites(c, s, junctionSites); + break; + } + } +} + +class SiteRecord { + public: + Site* site; + Value* value; +}; + +void +init(SiteRecord* r, Site* s, Value* v) +{ + r->site = s; + r->value = v; +} + +class SiteRecordList { + public: + SiteRecordList(SiteRecord* records, unsigned capacity): + records(records), index(0), capacity(capacity) + { } + + SiteRecord* records; + unsigned index; + unsigned capacity; +}; + +void +freeze(Context* c, SiteRecordList* frozen, Site* s, Value* v) +{ + assert(c, frozen->index < frozen->capacity); + + s->freeze(c, v); + init(new (frozen->records + (frozen->index ++)) SiteRecord, s, v); +} + +void +thaw(Context* c, SiteRecordList* frozen) +{ + while (frozen->index) { + SiteRecord* sr = frozen->records + (-- frozen->index); + sr->site->thaw(c, sr->value); + } +} + +bool +resolveOriginalSites(Context* c, Event* e, SiteRecordList* frozen, + Site** sites) +{ + bool complete = true; + for (FrameIterator it(c, e->stackAfter, e->localsAfter, true); + it.hasMore();) + { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = v ? live(c, v) : 0; + Site* s = sites[el.localIndex]; + + if (r) { + if (s) { + if (DebugControl) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, "resolve original %s for %p local %d frame %d\n", + buffer, v, el.localIndex, el.frameIndex(c)); + } + + Site* target = pickSiteOrMove + (c, v, s->mask(c), true, true, ResolveRegisterReserveCount); + + freeze(c, frozen, target, v); + } else { + complete = false; + } + } else if (s) { + if (DebugControl) { + char buffer[256]; + s->toString(c, buffer, 256); + fprintf(stderr, "freeze original %s for %p local %d frame %d\n", + buffer, v, el.localIndex, el.frameIndex(c)); + } + + Value dummy(0, 0, lir::ValueGeneral); + dummy.addSite(c, s); + dummy.removeSite(c, s); + freeze(c, frozen, s, 0); + } + } + + return complete; +} + +bool +resolveSourceSites(Context* c, Event* e, SiteRecordList* frozen, Site** sites) +{ + bool complete = true; + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = live(c, v); + + if (r and sites[el.localIndex] == 0) { + SiteMask mask((1 << lir::RegisterOperand) | (1 << lir::MemoryOperand), + c->regFile->generalRegisters.mask, AnyFrameIndex); + + Site* s = pickSourceSite + (c, r, 0, 0, &mask, true, false, true, acceptForResolve); + + if (s) { + if (DebugControl) { + char buffer[256]; s->toString(c, buffer, 256); + fprintf(stderr, "resolve source %s from %p local %d frame %d\n", + buffer, v, el.localIndex, el.frameIndex(c)); + } + + freeze(c, frozen, s, v); + + sites[el.localIndex] = s->copy(c); + } else { + complete = false; + } + } + } + + return complete; +} + +void +resolveTargetSites(Context* c, Event* e, SiteRecordList* frozen, Site** sites) +{ + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + Value* v = el.value; + Read* r = live(c, v); + + if (r and sites[el.localIndex] == 0) { + SiteMask mask((1 << lir::RegisterOperand) | (1 << lir::MemoryOperand), + c->regFile->generalRegisters.mask, AnyFrameIndex); + + Site* s = pickSourceSite + (c, r, 0, 0, &mask, false, true, true, acceptForResolve); + + if (s == 0) { + s = maybeMove(c, v, mask, false, true, ResolveRegisterReserveCount); + } + + freeze(c, frozen, s, v); + + sites[el.localIndex] = s->copy(c); + + if (DebugControl) { + char buffer[256]; sites[el.localIndex]->toString(c, buffer, 256); + fprintf(stderr, "resolve target %s for %p local %d frame %d\n", + buffer, el.value, el.localIndex, el.frameIndex(c)); + } + } + } +} + +void +resolveJunctionSites(Context* c, Event* e, SiteRecordList* frozen) +{ + bool complete; + if (e->junctionSites) { + complete = resolveOriginalSites(c, e, frozen, e->junctionSites); + } else { + propagateJunctionSites(c, e); + complete = false; + } + + if (e->junctionSites) { + if (not complete) { + complete = resolveSourceSites(c, e, frozen, e->junctionSites); + if (not complete) { + resolveTargetSites(c, e, frozen, e->junctionSites); + } + } + + if (DebugControl) { + fprintf(stderr, "resolved junction sites %p at %d\n", + e->junctionSites, e->logicalInstruction->index); + } + } +} + +void +resolveBranchSites(Context* c, Event* e, SiteRecordList* frozen) +{ + if (e->successors->nextSuccessor and e->junctionSites == 0) { + unsigned footprint = frameFootprint(c, e->stackAfter); + RUNTIME_ARRAY(Site*, branchSites, footprint); + memset(RUNTIME_ARRAY_BODY(branchSites), 0, sizeof(Site*) * footprint); + + if (not resolveSourceSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites))) + { + resolveTargetSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites)); + } + } +} + +void +captureBranchSnapshots(Context* c, Event* e) +{ + if (e->successors->nextSuccessor) { + for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { + FrameIterator::Element el = it.next(c); + e->snapshots = makeSnapshots(c, el.value, e->snapshots); + } + + for (Cell* sv = e->successors->forkState->saved; sv; sv = sv->next) { + e->snapshots = makeSnapshots(c, sv->value, e->snapshots); + } + + if (DebugControl) { + fprintf(stderr, "captured snapshots %p at %d\n", + e->snapshots, e->logicalInstruction->index); + } + } +} + +void +populateSiteTables(Context* c, Event* e, SiteRecordList* frozen) +{ + resolveJunctionSites(c, e, frozen); + + resolveBranchSites(c, e, frozen); +} + +void +setSites(Context* c, Value* v, Site* s) +{ + assert(c, live(c, v)); + + for (; s; s = s->next) { + v->addSite(c, s->copy(c)); + } + + if (DebugControl) { + char buffer[256]; sitesToString(c, v->sites, buffer, 256); + fprintf(stderr, "set sites %s for %p\n", buffer, v); + } +} + +void +resetFrame(Context* c, Event* e) +{ + for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { + FrameIterator::Element el = it.next(c); + el.value->clearSites(c); + } + + while (c->acquiredResources) { + c->acquiredResources->value->clearSites(c); + } +} + +void +setSites(Context* c, Event* e, Site** sites) +{ + resetFrame(c, e); + + for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { + FrameIterator::Element el = it.next(c); + if (sites[el.localIndex]) { + if (live(c, el.value)) { + setSites(c, el.value, sites[el.localIndex]); + } else if (DebugControl) { + char buffer[256]; sitesToString(c, sites[el.localIndex], buffer, 256); + fprintf(stderr, "skip sites %s for %p local %d frame %d\n", + buffer, el.value, el.localIndex, el.frameIndex(c)); + } + } else if (DebugControl) { + fprintf(stderr, "no sites for %p local %d frame %d\n", + el.value, el.localIndex, el.frameIndex(c)); + } + } +} + +void +removeBuddies(Context* c) +{ + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element el = it.next(c); + removeBuddy(c, el.value); + } +} + +void +restore(Context* c, Event* e, Snapshot* snapshots) +{ + for (Snapshot* s = snapshots; s; s = s->next) { + Value* v = s->value; + Value* next = v->buddy; + if (v != next) { + v->buddy = v; + Value* p = next; + while (p->buddy != v) p = p->buddy; + p->buddy = next; + } + } + + for (Snapshot* s = snapshots; s; s = s->next) { + assert(c, s->buddy); + + s->value->buddy = s->buddy; + } + + resetFrame(c, e); + + for (Snapshot* s = snapshots; s; s = s->next) { + if (live(c, s->value)) { + if (live(c, s->value) and s->sites and s->value->sites == 0) { + setSites(c, s->value, s->sites); + } + } + + // 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(c, s->value)); + } +} + +void +populateSources(Context* c, Event* e) +{ + RUNTIME_ARRAY(SiteRecord, frozenRecords, e->readCount); + SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), e->readCount); + + for (Read* r = e->reads; r; r = r->eventNext) { + r->value->source = readSource(c, r); + if (r->value->source) { + if (DebugReads) { + char buffer[256]; r->value->source->toString(c, buffer, 256); + fprintf(stderr, "freeze source %s for %p\n", + buffer, r->value); + } + + freeze(c, &frozen, r->value->source, r->value); + } + } + + thaw(c, &frozen); +} + +void +setStubRead(Context* c, StubReadPair* p, Value* v) +{ + if (v) { + StubRead* r = stubRead(c); + if (DebugReads) { + fprintf(stderr, "add stub read %p to %p\n", r, v); + } + // TODO: this is rather icky looking... but despite how it looks, it will not cause an NPE + ((Event*)0)->addRead(c, v, r); + + p->value = v; + p->read = r; + } +} + +void +populateJunctionReads(Context* c, Link* link) +{ + JunctionState* state = new + (c->zone->allocate + (sizeof(JunctionState) + + (sizeof(StubReadPair) * frameFootprint(c, c->stack)))) + JunctionState(frameFootprint(c, c->stack)); + + memset(state->reads, 0, sizeof(StubReadPair) * frameFootprint(c, c->stack)); + + link->junctionState = state; + + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + setStubRead(c, state->reads + e.localIndex, e.value); + } +} + +void +updateJunctionReads(Context* c, JunctionState* state) +{ + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + StubReadPair* p = state->reads + e.localIndex; + if (p->value and p->read->read == 0) { + Read* r = live(c, e.value); + if (r) { + if (DebugReads) { + fprintf(stderr, "stub read %p for %p valid: %p\n", + p->read, p->value, r); + } + p->read->read = r; + } + } + } + + for (unsigned i = 0; i < frameFootprint(c, c->stack); ++i) { + StubReadPair* p = state->reads + i; + if (p->value and p->read->read == 0) { + if (DebugReads) { + fprintf(stderr, "stub read %p for %p invalid\n", p->read, p->value); + } + p->read->valid_ = false; + } + } +} + +void +compile(Context* c, uintptr_t stackOverflowHandler, unsigned stackLimitOffset) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + Assembler* a = c->assembler; + + Block* firstBlock = block(c, c->firstEvent); + Block* block = firstBlock; + + if (stackOverflowHandler) { + a->checkStackOverflow(stackOverflowHandler, stackLimitOffset); + } + + a->allocateFrame(c->alignedFrameSize); + + for (Event* e = c->firstEvent; e; e = e->next) { + if (DebugCompile) { + fprintf(stderr, + " -- compile %s at %d with %d preds %d succs %d stack\n", + e->name(), e->logicalInstruction->index, + e->predecessors->countPredecessors(), + e->successors->countSuccessors(), + e->stackBefore ? e->stackBefore->index + 1 : 0); + } + + e->block = block; + + c->stack = e->stackBefore; + c->locals = e->localsBefore; + + if (e->logicalInstruction->machineOffset == 0) { + e->logicalInstruction->machineOffset = a->offset(); + } + + if (e->predecessors) { + visit(c, e->predecessors->lastPredecessor()); + + Event* first = e->predecessors->predecessor; + if (e->predecessors->nextPredecessor) { + for (Link* pl = e->predecessors; + pl->nextPredecessor; + pl = pl->nextPredecessor) + { + updateJunctionReads(c, pl->junctionState); + } + + if (DebugControl) { + fprintf(stderr, "set sites to junction sites %p at %d\n", + first->junctionSites, first->logicalInstruction->index); + } + + setSites(c, e, first->junctionSites); + removeBuddies(c); + } else if (first->successors->nextSuccessor) { + if (DebugControl) { + fprintf(stderr, "restore snapshots %p at %d\n", + first->snapshots, first->logicalInstruction->index); + } + + restore(c, e, first->snapshots); + } + } + + unsigned footprint = frameFootprint(c, e->stackAfter); + RUNTIME_ARRAY(SiteRecord, frozenRecords, footprint); + SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), footprint); + + bool branch = e->isBranch(); + if (branch and e->successors) { + populateSiteTables(c, e, &frozen); + } + + populateSources(c, e); + + if (branch and e->successors) { + captureBranchSnapshots(c, e); + } + + thaw(c, &frozen); + + e->compile(c); + + if ((not branch) and e->successors) { + populateSiteTables(c, e, &frozen); + captureBranchSnapshots(c, e); + thaw(c, &frozen); + } + + if (e->visitLinks) { + for (Cell* cell = reverseDestroy(e->visitLinks); cell; cell = cell->next) { + visit(c, cell->value); + } + e->visitLinks = 0; + } + + for (CodePromise* p = e->promises; p; p = p->next) { + p->offset = a->offset(); + } + + a->endEvent(); + + LogicalInstruction* nextInstruction = e->logicalInstruction->next(c); + if (e->next == 0 + or (e->next->logicalInstruction != e->logicalInstruction + and (e->next->logicalInstruction != nextInstruction + or e != e->logicalInstruction->lastEvent))) + { + Block* b = e->logicalInstruction->firstEvent->block; + + while (b->nextBlock) { + b = b->nextBlock; + } + + if (b != block) { + b->nextBlock = block; + } + + block->nextInstruction = nextInstruction; + block->assemblerBlock = a->endBlock(e->next != 0); + + if (e->next) { + block = compiler::block(c, e->next); + } + } + } + + c->firstBlock = firstBlock; +} + +void +restore(Context* c, ForkState* state) +{ + for (unsigned i = 0; i < state->readCount; ++i) { + ForkElement* p = state->elements + i; + p->value->lastRead = p->read; + p->read->allocateTarget(c); + } +} + +void +addForkElement(Context* c, Value* v, ForkState* state, unsigned index) +{ + MultiRead* r = multiRead(c); + if (DebugReads) { + fprintf(stderr, "add multi read %p to %p\n", r, v); + } + // TODO: this is rather icky looking... but despite how it looks, it will not cause an NPE + ((Event*)0)->addRead(c, v, r); + + ForkElement* p = state->elements + index; + p->value = v; + p->read = r; +} + +ForkState* +saveState(Context* c) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + unsigned elementCount = frameFootprint(c, c->stack) + count(c->saved); + + ForkState* state = new + (c->zone->allocate + (sizeof(ForkState) + (sizeof(ForkElement) * elementCount))) + ForkState(c->stack, c->locals, c->saved, c->predecessor, c->logicalIp); + + if (c->predecessor) { + c->forkState = state; + + unsigned count = 0; + + for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + addForkElement(c, e.value, state, count++); + } + + for (Cell* sv = c->saved; sv; sv = sv->next) { + addForkElement(c, sv->value, state, count++); + } + + state->readCount = count; + } + + c->saved = 0; + + return state; +} + +void +restoreState(Context* c, ForkState* s) +{ + if (c->logicalCode[c->logicalIp]->lastEvent == 0) { + appendDummy(c); + } + + c->stack = s->stack; + c->locals = s->locals; + c->predecessor = s->predecessor; + c->logicalIp = s->logicalIp; + + if (c->predecessor) { + c->forkState = s; + restore(c, s); + } +} + +Value* +maybeBuddy(Context* c, Value* v) +{ + if (v->home >= 0) { + Value* n = value(c, v->type); + appendBuddy(c, v, n); + return n; + } else { + return v; + } +} + +void +linkLocals(Context* c, Local* oldLocals, Local* newLocals) +{ + for (int i = 0; i < static_cast(c->localFootprint); ++i) { + Local* local = oldLocals + i; + if (local->value) { + int highOffset = c->arch->bigEndian() ? 1 : -1; + + if (i + highOffset >= 0 + and i + highOffset < static_cast(c->localFootprint) + and local->value->nextWord == local[highOffset].value) + { + Value* v = newLocals[i].value; + Value* next = newLocals[i + highOffset].value; + v->nextWord = next; + next->nextWord = v; + next->wordIndex = 1; + } + } + } +} + +class Client: public Assembler::Client { + public: + Client(Context* c): c(c) { } + + virtual int acquireTemporary(uint32_t mask) { + unsigned cost; + int r = pickRegisterTarget(c, 0, mask, &cost); + expect(c, cost < Target::Impossible); + save(r); + c->registerResources[r].increment(c); + return r; + } + + virtual void releaseTemporary(int r) { + c->registerResources[r].decrement(c); + } + + virtual void save(int r) { + RegisterResource* reg = c->registerResources + r; + + assert(c, reg->referenceCount == 0); + assert(c, reg->freezeCount == 0); + assert(c, not reg->reserved); + + if (reg->value) { + steal(c, reg, 0); + } + } + + Context* c; +}; + +class MyCompiler: public Compiler { + public: + MyCompiler(System* s, Assembler* assembler, Zone* zone, + Compiler::Client* compilerClient): + c(s, assembler, zone, compilerClient), client(&c) + { + assembler->setClient(&client); + } + + virtual State* saveState() { + State* s = compiler::saveState(&c); + restoreState(s); + return s; + } + + virtual void restoreState(State* state) { + compiler::restoreState(&c, static_cast(state)); + } + + virtual Subroutine* startSubroutine() { + return c.subroutine = new(c.zone) MySubroutine; + } + + virtual void returnFromSubroutine(Subroutine* subroutine, Operand* address) { + appendSaveLocals(&c); + appendJump(&c, lir::Jump, static_cast(address), false, true); + static_cast(subroutine)->forkState = compiler::saveState(&c); + } + + virtual void linkSubroutine(Subroutine* subroutine) { + Local* oldLocals = c.locals; + restoreState(static_cast(subroutine)->forkState); + linkLocals(&c, oldLocals, c.locals); + } + + virtual void init(unsigned logicalCodeLength, unsigned parameterFootprint, + unsigned localFootprint, unsigned alignedFrameSize) + { + c.logicalCodeLength = logicalCodeLength; + c.parameterFootprint = parameterFootprint; + c.localFootprint = localFootprint; + c.alignedFrameSize = alignedFrameSize; + + unsigned frameResourceCount = totalFrameSize(&c); + + c.frameResources = static_cast + (c.zone->allocate(sizeof(FrameResource) * frameResourceCount)); + + for (unsigned i = 0; i < frameResourceCount; ++i) { + new (c.frameResources + i) FrameResource; + } + + unsigned base = frameBase(&c); + c.frameResources[base + c.arch->returnAddressOffset()].reserved = true; + c.frameResources[base + c.arch->framePointerOffset()].reserved + = UseFramePointer; + + // leave room for logical instruction -1 + unsigned codeSize = sizeof(LogicalInstruction*) * (logicalCodeLength + 1); + c.logicalCode = static_cast + (c.zone->allocate(codeSize)); + memset(c.logicalCode, 0, codeSize); + c.logicalCode++; + + c.locals = static_cast + (c.zone->allocate(sizeof(Local) * localFootprint)); + + memset(c.locals, 0, sizeof(Local) * localFootprint); + + c.logicalCode[-1] = new(c.zone) LogicalInstruction(-1, c.stack, c.locals); + } + + virtual void visitLogicalIp(unsigned logicalIp) { + assert(&c, logicalIp < c.logicalCodeLength); + + if (c.logicalCode[c.logicalIp]->lastEvent == 0) { + appendDummy(&c); + } + + Event* e = c.logicalCode[logicalIp]->firstEvent; + + Event* p = c.predecessor; + if (p) { + if (DebugAppend) { + fprintf(stderr, "visit %d pred %d\n", logicalIp, + p->logicalInstruction->index); + } + + p->stackAfter = c.stack; + p->localsAfter = c.locals; + + Link* link = compiler::link + (&c, p, e->predecessors, e, p->successors, c.forkState); + e->predecessors = link; + p->successors = link; + c.lastEvent->visitLinks = cons(&c, link, c.lastEvent->visitLinks); + + if (DebugAppend) { + fprintf(stderr, "populate junction reads for %d to %d\n", + p->logicalInstruction->index, logicalIp); + } + + populateJunctionReads(&c, link); + } + + if (c.subroutine) { + c.subroutine->forkState + = c.logicalCode[logicalIp]->subroutine->forkState; + c.subroutine = 0; + } + + c.forkState = 0; + } + + virtual void startLogicalIp(unsigned logicalIp) { + assert(&c, logicalIp < c.logicalCodeLength); + assert(&c, c.logicalCode[logicalIp] == 0); + + if (c.logicalCode[c.logicalIp]->lastEvent == 0) { + appendDummy(&c); + } + + Event* p = c.predecessor; + if (p) { + p->stackAfter = c.stack; + p->localsAfter = c.locals; + } + + c.logicalCode[logicalIp] = new(c.zone) LogicalInstruction(logicalIp, c.stack, c.locals); + + 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, IntegerType); + } + } + } + } + + virtual Promise* machineIp(unsigned logicalIp) { + return ipPromise(&c, logicalIp); + } + + virtual Promise* poolAppend(intptr_t value) { + return poolAppendPromise(resolvedPromise(&c, value)); + } + + virtual Promise* poolAppendPromise(Promise* value) { + Promise* p = poolPromise(&c, c.constantCount); + + ConstantPoolNode* constant = new (c.zone) ConstantPoolNode(value); + + if (c.firstConstant) { + c.lastConstant->next = constant; + } else { + c.firstConstant = constant; + } + c.lastConstant = constant; + ++ c.constantCount; + + return p; + } + + virtual Operand* constant(int64_t value, Compiler::OperandType type) { + return promiseConstant(resolvedPromise(&c, value), type); + } + + virtual Operand* promiseConstant(Promise* value, Compiler::OperandType type) { + return compiler::value + (&c, valueType(&c, type), compiler::constantSite(&c, value)); + } + + virtual Operand* address(Promise* address) { + return value(&c, lir::ValueGeneral, compiler::addressSite(&c, address)); + } + + virtual Operand* memory(Operand* base, + OperandType type, + int displacement = 0, + Operand* index = 0, + unsigned scale = 1) + { + Value* result = value(&c, valueType(&c, type)); + + appendMemory(&c, static_cast(base), displacement, + static_cast(index), scale, result); + + return result; + } + + virtual Operand* register_(int number) { + return compiler::register_(&c, number); + } + + Promise* machineIp() { + return c.logicalCode[c.logicalIp]->lastEvent->makeCodePromise(&c); + } + + virtual void push(unsigned footprint UNUSED) { + assert(&c, footprint == 1); + + Value* v = value(&c, lir::ValueGeneral); + Stack* s = compiler::stack(&c, v, c.stack); + + v->home = frameIndex(&c, s->index + c.localFootprint); + c.stack = s; + } + + virtual void push(unsigned footprint, Operand* value) { + compiler::push(&c, footprint, static_cast(value)); + } + + virtual void save(unsigned footprint, Operand* value) { + c.saved = cons(&c, static_cast(value), c.saved); + if (TargetBytesPerWord == 4 and footprint > 1) { + assert(&c, footprint == 2); + assert(&c, static_cast(value)->nextWord); + + save(1, static_cast(value)->nextWord); + } + } + + virtual Operand* pop(unsigned footprint) { + return compiler::pop(&c, footprint); + } + + virtual void pushed() { + Value* v = value(&c, lir::ValueGeneral); + appendFrameSite + (&c, v, frameIndex + (&c, (c.stack ? c.stack->index : 0) + c.localFootprint)); + + Stack* s = compiler::stack(&c, v, c.stack); + v->home = frameIndex(&c, s->index + c.localFootprint); + c.stack = s; + } + + virtual void popped(unsigned footprint) { + for (; footprint; -- footprint) { + assert(&c, c.stack->value == 0 or c.stack->value->home >= 0); + + if (DebugFrame) { + fprintf(stderr, "popped %p\n", c.stack->value); + } + + c.stack = c.stack->next; + } + } + + virtual unsigned topOfStack() { + return c.stack->index; + } + + virtual Operand* peek(unsigned footprint, unsigned index) { + Stack* s = c.stack; + for (unsigned i = index; i > 0; --i) { + s = s->next; + } + + if (footprint > 1) { + assert(&c, footprint == 2); + + bool bigEndian = c.arch->bigEndian(); + +#ifndef NDEBUG + Stack* low; + Stack* high; + if (bigEndian) { + high = s; + low = s->next; + } else { + low = s; + high = s->next; + } + + assert(&c, (TargetBytesPerWord == 8 + and low->value->nextWord == low->value and high->value == 0) + or (TargetBytesPerWord == 4 + and low->value->nextWord == high->value)); +#endif // not NDEBUG + + if (bigEndian) { + s = s->next; + } + } + + return s->value; + } + + virtual Operand* call(Operand* address, + unsigned flags, + TraceHandler* traceHandler, + unsigned resultSize, + OperandType resultType, + unsigned argumentCount, + ...) + { + va_list a; va_start(a, argumentCount); + + bool bigEndian = c.arch->bigEndian(); + + unsigned footprint = 0; + unsigned size = TargetBytesPerWord; + RUNTIME_ARRAY(Value*, arguments, argumentCount); + int index = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + Value* o = va_arg(a, Value*); + if (o) { + if (bigEndian and size > TargetBytesPerWord) { + RUNTIME_ARRAY_BODY(arguments)[index++] = o->nextWord; + } + RUNTIME_ARRAY_BODY(arguments)[index] = o; + if ((not bigEndian) and size > TargetBytesPerWord) { + RUNTIME_ARRAY_BODY(arguments)[++index] = o->nextWord; + } + size = TargetBytesPerWord; + ++ index; + } else { + size = 8; + } + ++ footprint; + } + + va_end(a); + + Stack* argumentStack = c.stack; + for (int i = index - 1; i >= 0; --i) { + argumentStack = compiler::stack + (&c, RUNTIME_ARRAY_BODY(arguments)[i], argumentStack); + } + + Value* result = value(&c, valueType(&c, resultType)); + appendCall(&c, static_cast(address), flags, traceHandler, result, + resultSize, argumentStack, index, 0); + + return result; + } + + virtual Operand* stackCall(Operand* address, + unsigned flags, + TraceHandler* traceHandler, + unsigned resultSize, + OperandType resultType, + unsigned argumentFootprint) + { + Value* result = value(&c, valueType(&c, resultType)); + appendCall(&c, static_cast(address), flags, traceHandler, result, + resultSize, c.stack, 0, argumentFootprint); + return result; + } + + virtual void return_(unsigned size, Operand* value) { + appendReturn(&c, size, static_cast(value)); + } + + virtual void initLocal(unsigned footprint, unsigned index, OperandType type) + { + assert(&c, index + footprint <= c.localFootprint); + + Value* v = value(&c, valueType(&c, type)); + + if (footprint > 1) { + assert(&c, footprint == 2); + + unsigned highIndex; + unsigned lowIndex; + if (c.arch->bigEndian()) { + highIndex = index + 1; + lowIndex = index; + } else { + lowIndex = index + 1; + highIndex = index; + } + + if (TargetBytesPerWord == 4) { + initLocal(1, highIndex, type); + Value* next = c.locals[highIndex].value; + v->nextWord = next; + next->nextWord = v; + next->wordIndex = 1; + } + + index = lowIndex; + } + + if (DebugFrame) { + fprintf(stderr, "init local %p at %d (%d)\n", + v, index, frameIndex(&c, index)); + } + + appendFrameSite(&c, v, frameIndex(&c, index)); + + Local* local = c.locals + index; + local->value = v; + v->home = frameIndex(&c, index); + } + + virtual void initLocalsFromLogicalIp(unsigned logicalIp) { + assert(&c, logicalIp < c.logicalCodeLength); + + unsigned footprint = sizeof(Local) * c.localFootprint; + Local* newLocals = static_cast(c.zone->allocate(footprint)); + memset(newLocals, 0, footprint); + c.locals = newLocals; + + Event* e = c.logicalCode[logicalIp]->firstEvent; + for (int i = 0; i < static_cast(c.localFootprint); ++i) { + Local* local = e->locals() + i; + if (local->value) { + initLocal + (1, i, local->value->type == lir::ValueGeneral ? IntegerType : FloatType); + } + } + + linkLocals(&c, e->locals(), newLocals); + } + + virtual void storeLocal(unsigned footprint, Operand* src, unsigned index) { + compiler::storeLocal(&c, footprint, static_cast(src), index, true); + } + + virtual Operand* loadLocal(unsigned footprint, unsigned index) { + return compiler::loadLocal(&c, footprint, index); + } + + virtual void saveLocals() { + appendSaveLocals(&c); + } + + virtual void checkBounds(Operand* object, unsigned lengthOffset, + Operand* index, intptr_t handler) + { + appendBoundsCheck(&c, static_cast(object), lengthOffset, + static_cast(index), handler); + } + + virtual void store(unsigned srcSize, Operand* src, unsigned dstSize, + Operand* dst) + { + appendMove(&c, lir::Move, srcSize, srcSize, static_cast(src), + dstSize, static_cast(dst)); + } + + virtual Operand* load(unsigned srcSize, unsigned srcSelectSize, Operand* src, + unsigned dstSize) + { + assert(&c, dstSize >= TargetBytesPerWord); + + Value* dst = value(&c, static_cast(src)->type); + appendMove(&c, lir::Move, srcSize, srcSelectSize, static_cast(src), + dstSize, dst); + return dst; + } + + virtual Operand* loadz(unsigned srcSize, unsigned srcSelectSize, + Operand* src, unsigned dstSize) + { + assert(&c, dstSize >= TargetBytesPerWord); + + Value* dst = value(&c, static_cast(src)->type); + appendMove(&c, lir::MoveZ, srcSize, srcSelectSize, static_cast(src), + dstSize, dst); + return dst; + } + + virtual void jumpIfEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfNotEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfNotEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfLess(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfLess, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfGreater(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfGreater, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfLessOrEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfLessOrEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfGreaterOrEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + + appendBranch(&c, lir::JumpIfGreaterOrEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatNotEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatNotEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatLess(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatLess, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatGreater(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatGreater, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatLessOrEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatLessOrEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatGreaterOrEqual(unsigned size, Operand* a, Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatGreaterOrEqual, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatLessOrUnordered(unsigned size, Operand* a, + Operand* b, Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatLessOrUnordered, size, static_cast(a), + static_cast(b), static_cast(address)); + } + + virtual void jumpIfFloatGreaterOrUnordered(unsigned size, Operand* a, + Operand* b, Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatGreaterOrUnordered, size, + static_cast(a), static_cast(b), + static_cast(address)); + } + + virtual void jumpIfFloatLessOrEqualOrUnordered(unsigned size, Operand* a, + Operand* b, Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatLessOrEqualOrUnordered, size, + static_cast(a), static_cast(b), + static_cast(address)); + } + + virtual void jumpIfFloatGreaterOrEqualOrUnordered(unsigned size, Operand* a, + Operand* b, + Operand* address) + { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + + appendBranch(&c, lir::JumpIfFloatGreaterOrEqualOrUnordered, size, + static_cast(a), static_cast(b), + static_cast(address)); + } + + virtual void jmp(Operand* address) { + appendJump(&c, lir::Jump, static_cast(address)); + } + + virtual void exit(Operand* address) { + appendJump(&c, lir::Jump, static_cast(address), true); + } + + virtual Operand* add(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Add, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* sub(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Subtract, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* mul(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Multiply, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* div(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Divide, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* rem(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral + and static_cast(b)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Remainder, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* fadd(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + static_cast(a)->type = static_cast(b)->type = lir::ValueFloat; + appendCombine(&c, lir::FloatAdd, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* fsub(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + static_cast(a)->type = static_cast(b)->type = lir::ValueFloat; + appendCombine(&c, lir::FloatSubtract, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* fmul(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + static_cast(a)->type = static_cast(b)->type = lir::ValueFloat; + appendCombine(&c, lir::FloatMultiply, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* fdiv(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendCombine(&c, lir::FloatDivide, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* frem(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueFloat + and static_cast(b)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendCombine(&c, lir::FloatRemainder, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* shl(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::ShiftLeft, TargetBytesPerWord, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* shr(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::ShiftRight, TargetBytesPerWord, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* ushr(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine + (&c, lir::UnsignedShiftRight, TargetBytesPerWord, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* and_(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::And, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* or_(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Or, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* xor_(unsigned size, Operand* a, Operand* b) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendCombine(&c, lir::Xor, size, static_cast(a), + size, static_cast(b), size, result); + return result; + } + + virtual Operand* neg(unsigned size, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendTranslate(&c, lir::Negate, size, static_cast(a), size, result); + return result; + } + + virtual Operand* fneg(unsigned size, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendTranslate(&c, lir::FloatNegate, size, static_cast(a), size, result); + return result; + } + + virtual Operand* abs(unsigned size, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueGeneral); + appendTranslate(&c, lir::Absolute, size, static_cast(a), size, result); + return result; + } + + virtual Operand* fabs(unsigned size, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendTranslate + (&c, lir::FloatAbsolute, size, static_cast(a), size, result); + return result; + } + + virtual Operand* fsqrt(unsigned size, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendTranslate + (&c, lir::FloatSquareRoot, size, static_cast(a), size, result); + return result; + } + + virtual Operand* f2f(unsigned aSize, unsigned resSize, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueFloat); + appendTranslate + (&c, lir::Float2Float, aSize, static_cast(a), resSize, result); + return result; + } + + virtual Operand* f2i(unsigned aSize, unsigned resSize, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueFloat); + Value* result = value(&c, lir::ValueGeneral); + appendTranslate + (&c, lir::Float2Int, aSize, static_cast(a), resSize, result); + return result; + } + + virtual Operand* i2f(unsigned aSize, unsigned resSize, Operand* a) { + assert(&c, static_cast(a)->type == lir::ValueGeneral); + Value* result = value(&c, lir::ValueFloat); + appendTranslate + (&c, lir::Int2Float, aSize, static_cast(a), resSize, result); + return result; + } + + virtual void trap() { + appendOperation(&c, lir::Trap); + } + + virtual void loadBarrier() { + appendOperation(&c, lir::LoadBarrier); + } + + virtual void storeStoreBarrier() { + appendOperation(&c, lir::StoreStoreBarrier); + } + + virtual void storeLoadBarrier() { + appendOperation(&c, lir::StoreLoadBarrier); + } + + virtual void compile(uintptr_t stackOverflowHandler, + unsigned stackLimitOffset) + { + compiler::compile(&c, stackOverflowHandler, stackLimitOffset); + } + + virtual unsigned resolve(uint8_t* dst) { + c.machineCode = dst; + c.assembler->setDestination(dst); + + Block* block = c.firstBlock; + while (block->nextBlock or block->nextInstruction) { + Block* next = block->nextBlock + ? block->nextBlock + : block->nextInstruction->firstEvent->block; + + next->start = block->assemblerBlock->resolve + (block->start, next->assemblerBlock); + + block = next; + } + + return c.machineCodeSize = block->assemblerBlock->resolve + (block->start, 0) + c.assembler->footerSize(); + } + + virtual unsigned poolSize() { + return c.constantCount * TargetBytesPerWord; + } + + virtual void write() { + c.assembler->write(); + + int i = 0; + for (ConstantPoolNode* n = c.firstConstant; n; n = n->next) { + target_intptr_t* target = reinterpret_cast + (c.machineCode + pad(c.machineCodeSize, TargetBytesPerWord) + i); + + if (n->promise->resolved()) { + *target = targetVW(n->promise->value()); + } else { + class Listener: public Promise::Listener { + public: + Listener(target_intptr_t* target): target(target){ } + + virtual bool resolve(int64_t value, void** location) { + *target = targetVW(value); + if (location) *location = target; + return true; + } + + target_intptr_t* target; + }; + new (n->promise->listen(sizeof(Listener))) Listener(target); + } + + i += TargetBytesPerWord; + } + } + + virtual void dispose() { + // ignore + } + + Context c; + compiler::Client client; +}; + +} // namespace compiler + +Compiler* +makeCompiler(System* system, Assembler* assembler, Zone* zone, + Compiler::Client* client) +{ + return new(zone) compiler::MyCompiler(system, assembler, zone, client); +} + +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/context.cpp b/src/codegen/compiler/context.cpp new file mode 100644 index 0000000000..5d77fdcd77 --- /dev/null +++ b/src/codegen/compiler/context.cpp @@ -0,0 +1,70 @@ +/* Copyright (c) 2008-2012, 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 "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" + +#include + +namespace avian { +namespace codegen { +namespace compiler { + +Context::Context(vm::System* system, Assembler* assembler, vm::Zone* zone, + Compiler::Client* client): + system(system), + assembler(assembler), + arch(assembler->arch()), + zone(zone), + client(client), + stack(0), + locals(0), + saved(0), + predecessor(0), + logicalCode(0), + regFile(arch->registerFile()), + regAlloc(system, arch->registerFile()), + registerResources + (static_cast + (zone->allocate(sizeof(RegisterResource) * regFile->allRegisters.limit))), + frameResources(0), + acquiredResources(0), + firstConstant(0), + lastConstant(0), + machineCode(0), + firstEvent(0), + lastEvent(0), + forkState(0), + subroutine(0), + firstBlock(0), + logicalIp(-1), + constantCount(0), + logicalCodeLength(0), + parameterFootprint(0), + localFootprint(0), + machineCodeSize(0), + alignedFrameSize(0), + availableGeneralRegisterCount(regFile->generalRegisters.limit - regFile->generalRegisters.start) +{ + for (unsigned i = regFile->generalRegisters.start; i < regFile->generalRegisters.limit; ++i) { + new (registerResources + i) RegisterResource(arch->reserved(i)); + + if (registerResources[i].reserved) { + -- availableGeneralRegisterCount; + } + } + for (unsigned i = regFile->floatRegisters.start; i < regFile->floatRegisters.limit; ++i) { + new (registerResources + i) RegisterResource(arch->reserved(i)); + } +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/context.h b/src/codegen/compiler/context.h new file mode 100644 index 0000000000..cd19097c42 --- /dev/null +++ b/src/codegen/compiler/context.h @@ -0,0 +1,122 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_CONTEXT_H +#define AVIAN_CODEGEN_COMPILER_CONTEXT_H + +#include +#include + +#include "regalloc.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace compiler { + +class Stack; +class Local; +class Event; +class LogicalInstruction; + +class Resource; +class RegisterResource; +class FrameResource; + +class ConstantPoolNode; + +class ForkState; +class MySubroutine; +class Block; + +template +class Cell { + public: + Cell(Cell* next, T* value): next(next), value(value) { } + + Cell* next; + T* value; +}; + +template +unsigned count(Cell* c) { + unsigned count = 0; + while (c) { + ++ count; + c = c->next; + } + return count; +} + +template +Cell* reverseDestroy(Cell* cell) { + Cell* previous = 0; + while (cell) { + Cell* next = cell->next; + cell->next = previous; + previous = cell; + cell = next; + } + return previous; +} + +class Context { + public: + Context(vm::System* system, Assembler* assembler, vm::Zone* zone, + Compiler::Client* client); + + vm::System* system; + Assembler* assembler; + Architecture* arch; + vm::Zone* zone; + Compiler::Client* client; + Stack* stack; + Local* locals; + Cell* saved; + Event* predecessor; + LogicalInstruction** logicalCode; + const RegisterFile* regFile; + RegisterAllocator regAlloc; + RegisterResource* registerResources; + FrameResource* frameResources; + Resource* acquiredResources; + ConstantPoolNode* firstConstant; + ConstantPoolNode* lastConstant; + uint8_t* machineCode; + Event* firstEvent; + Event* lastEvent; + ForkState* forkState; + MySubroutine* subroutine; + Block* firstBlock; + int logicalIp; + unsigned constantCount; + unsigned logicalCodeLength; + unsigned parameterFootprint; + unsigned localFootprint; + unsigned machineCodeSize; + unsigned alignedFrameSize; + unsigned availableGeneralRegisterCount; +}; + +inline Aborter* getAborter(Context* c) { + return c->system; +} + +template +Cell* cons(Context* c, T* value, Cell* next) { + return new (c->zone) Cell(next, value); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_CONTEXT_H diff --git a/src/codegen/compiler/event.cpp b/src/codegen/compiler/event.cpp new file mode 100644 index 0000000000..d61a4d794e --- /dev/null +++ b/src/codegen/compiler/event.cpp @@ -0,0 +1,1694 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" +#include +#include + +#include "codegen/compiler/context.h" +#include "codegen/compiler/event.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/read.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/ir.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace compiler { + +SiteMask generalRegisterMask(Context* c); +SiteMask generalRegisterOrConstantMask(Context* c); + +CodePromise* codePromise(Context* c, Promise* offset); + +void saveLocals(Context* c, Event* e); + +void +apply(Context* c, lir::UnaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High); + +void +apply(Context* c, lir::BinaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High); + +void +apply(Context* c, lir::TernaryOperation op, + unsigned s1Size, Site* s1Low, Site* s1High, + unsigned s2Size, Site* s2Low, Site* s2High, + unsigned s3Size, Site* s3Low, Site* s3High); + + +void append(Context* c, Event* e); + + +void clean(Context* c, Event* e, Stack* stack, Local* locals, Read* reads, + unsigned popIndex); + +Read* live(Context* c UNUSED, Value* v); + +void popRead(Context* c, Event* e UNUSED, Value* v); + +void +maybeMove(Context* c, lir::BinaryOperation type, unsigned srcSize, + unsigned srcSelectSize, Value* src, unsigned dstSize, Value* dst, + const SiteMask& dstMask); + +Site* +maybeMove(Context* c, Value* v, const SiteMask& mask, bool intersectMask, + bool includeNextWord, unsigned registerReserveCount = 0); + +Site* +maybeMove(Context* c, Read* read, bool intersectRead, bool includeNextWord, + unsigned registerReserveCount = 0); +Site* +pickSiteOrMove(Context* c, Value* src, Value* dst, Site* nextWord, + unsigned index); + +void push(Context* c, unsigned footprint, Value* v); + +Site* +pickTargetSite(Context* c, Read* read, bool intersectRead = false, + unsigned registerReserveCount = 0, + CostCalculator* costCalculator = 0); +Value* +register_(Context* c, int number); + +Event::Event(Context* c): + next(0), stackBefore(c->stack), localsBefore(c->locals), + stackAfter(0), localsAfter(0), promises(0), reads(0), + junctionSites(0), snapshots(0), predecessors(0), successors(0), + visitLinks(0), block(0), logicalInstruction(c->logicalCode[c->logicalIp]), + readCount(0) +{ } + +void Event::addRead(Context* c, Value* v, Read* r) { + if (DebugReads) { + fprintf(stderr, "add read %p to %p last %p event %p (%s)\n", + r, v, v->lastRead, this, (this ? this->name() : 0)); + } + + r->value = v; + if (this) { + r->event = this; + r->eventNext = this->reads; + this->reads = r; + ++ this->readCount; + } + + if (v->lastRead) { + // if (DebugReads) { + // fprintf(stderr, "append %p to %p for %p\n", r, v->lastRead, v); + // } + + v->lastRead->append(c, r); + } else { + v->reads = r; + } + v->lastRead = r; +} + +void Event::addRead(Context* c, Value* v, const SiteMask& mask, Value* successor) { + this->addRead(c, v, read(c, mask, successor)); +} + +void Event::addReads(Context* c, Value* v, unsigned size, + const SiteMask& lowMask, Value* lowSuccessor, + const SiteMask& highMask, Value* highSuccessor) +{ + SingleRead* r = read(c, lowMask, lowSuccessor); + this->addRead(c, v, r); + if (size > vm::TargetBytesPerWord) { + r->high_ = v->nextWord; + this->addRead(c, v->nextWord, highMask, highSuccessor); + } +} + +void Event::addReads(Context* c, Value* v, unsigned size, + const SiteMask& lowMask, const SiteMask& highMask) +{ + this->addReads(c, v, size, lowMask, 0, highMask, 0); +} + +CodePromise* Event::makeCodePromise(Context* c) { + return this->promises = new(c->zone) CodePromise(c, this->promises); +} + +bool Event::isUnreachable() { + for (Link* p = this->predecessors; p; p = p->nextPredecessor) { + if (not p->predecessor->allExits()) return false; + } + return this->predecessors != 0; +} + +unsigned Link::countPredecessors() { + Link* link = this; + unsigned c = 0; + for (; link; link = link->nextPredecessor) { + ++ c; + } + return c; +} + +Link* Link::lastPredecessor() { + Link* link = this; + while (link->nextPredecessor) { + link = link->nextPredecessor; + } + return link; +} + +unsigned Link::countSuccessors() { + Link* link = this; + unsigned c = 0; + for (; link; link = link->nextSuccessor) { + ++ c; + } + return c; +} + +Link* link(Context* c, Event* predecessor, Link* nextPredecessor, Event* successor, + Link* nextSuccessor, ForkState* forkState) +{ + return new(c->zone) Link + (predecessor, nextPredecessor, successor, nextSuccessor, forkState); +} + + +class CallEvent: public Event { + public: + CallEvent(Context* c, Value* address, unsigned flags, + TraceHandler* traceHandler, Value* result, unsigned resultSize, + Stack* argumentStack, unsigned argumentCount, + unsigned stackArgumentFootprint): + Event(c), + address(address), + traceHandler(traceHandler), + result(result), + returnAddressSurrogate(0), + framePointerSurrogate(0), + popIndex(0), + stackArgumentIndex(0), + flags(flags), + resultSize(resultSize), + stackArgumentFootprint(stackArgumentFootprint) + { + uint32_t registerMask = c->regFile->generalRegisters.mask; + + if (argumentCount) { + assert(c, (flags & Compiler::TailJump) == 0); + assert(c, stackArgumentFootprint == 0); + + Stack* s = argumentStack; + unsigned index = 0; + unsigned argumentIndex = 0; + + while (true) { + unsigned footprint + = (argumentIndex + 1 < argumentCount + and s->value->nextWord == s->next->value) + ? 2 : 1; + + if (index % (c->arch->argumentAlignment() ? footprint : 1)) { + ++ index; + } + + SiteMask targetMask; + if (index + (c->arch->argumentRegisterAlignment() ? footprint : 1) + <= c->arch->argumentRegisterCount()) + { + int number = c->arch->argumentRegister(index); + + if (DebugReads) { + fprintf(stderr, "reg %d arg read %p\n", number, s->value); + } + + targetMask = SiteMask::fixedRegisterMask(number); + registerMask &= ~(1 << number); + } else { + if (index < c->arch->argumentRegisterCount()) { + index = c->arch->argumentRegisterCount(); + } + + unsigned frameIndex = index - c->arch->argumentRegisterCount(); + + if (DebugReads) { + fprintf(stderr, "stack %d arg read %p\n", frameIndex, s->value); + } + + targetMask = SiteMask(1 << lir::MemoryOperand, 0, frameIndex); + } + + this->addRead(c, s->value, targetMask); + + ++ index; + + if ((++ argumentIndex) < argumentCount) { + s = s->next; + } else { + break; + } + } + } + + if (DebugReads) { + fprintf(stderr, "address read %p\n", address); + } + + { bool thunk; + OperandMask op; + c->arch->plan + ((flags & Compiler::Aligned) ? lir::AlignedCall : lir::Call, vm::TargetBytesPerWord, + op, &thunk); + + assert(c, not thunk); + + this->addRead(c, address, SiteMask + (op.typeMask, registerMask & op.registerMask, AnyFrameIndex)); + } + + Stack* stack = stackBefore; + + if (stackArgumentFootprint) { + RUNTIME_ARRAY(Value*, arguments, stackArgumentFootprint); + for (int i = stackArgumentFootprint - 1; i >= 0; --i) { + Value* v = stack->value; + stack = stack->next; + + if ((vm::TargetBytesPerWord == 8 + and (v == 0 or (i >= 1 and stack->value == 0))) + or (vm::TargetBytesPerWord == 4 and v->nextWord != v)) + { + assert(c, vm::TargetBytesPerWord == 8 or v->nextWord == stack->value); + + RUNTIME_ARRAY_BODY(arguments)[i--] = stack->value; + stack = stack->next; + } + RUNTIME_ARRAY_BODY(arguments)[i] = v; + } + + 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(); + if (UseFramePointer) { + framePointerIndex = base + c->arch->framePointerOffset(); + } else { + framePointerIndex = -1; + } + + frameOffset = totalFrameSize(c) + - c->arch->argumentFootprint(stackArgumentFootprint); + } else { + returnAddressIndex = -1; + framePointerIndex = -1; + frameOffset = 0; + } + + for (unsigned i = 0; i < stackArgumentFootprint; ++i) { + Value* v = RUNTIME_ARRAY_BODY(arguments)[i]; + if (v) { + int frameIndex = i + frameOffset; + + if (DebugReads) { + fprintf(stderr, "stack arg read %p at %d of %d\n", + v, frameIndex, totalFrameSize(c)); + } + + if (static_cast(frameIndex) == returnAddressIndex) { + returnAddressSurrogate = v; + this->addRead(c, v, generalRegisterMask(c)); + } else if (static_cast(frameIndex) == framePointerIndex) { + framePointerSurrogate = v; + this->addRead(c, v, generalRegisterMask(c)); + } else { + this->addRead(c, v, SiteMask(1 << lir::MemoryOperand, 0, frameIndex)); + } + } + } + } + + 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 = compiler::frameIndex + (c, stack->index + c->localFootprint); + + if (DebugReads) { + fprintf(stderr, "stack save read %p at %d of %d\n", + stack->value, logicalIndex, totalFrameSize(c)); + } + + this->addRead(c, stack->value, SiteMask + (1 << lir::MemoryOperand, 0, logicalIndex)); + } + + stack = stack->next; + } + + saveLocals(c, this); + } + } + + virtual const char* name() { + return "CallEvent"; + } + + virtual void compile(Context* c) { + lir::UnaryOperation op; + + if (TailCalls and (flags & Compiler::TailJump)) { + if (flags & Compiler::LongJumpOrCall) { + if (flags & Compiler::Aligned) { + op = lir::AlignedLongJump; + } else { + op = lir::LongJump; + } + } else if (flags & Compiler::Aligned) { + op = lir::AlignedJump; + } else { + op = lir::Jump; + } + + assert(c, returnAddressSurrogate == 0 + or returnAddressSurrogate->source->type(c) == lir::RegisterOperand); + assert(c, framePointerSurrogate == 0 + or framePointerSurrogate->source->type(c) == lir::RegisterOperand); + + int ras; + if (returnAddressSurrogate) { + returnAddressSurrogate->source->freeze(c, returnAddressSurrogate); + + ras = static_cast + (returnAddressSurrogate->source)->number; + } else { + ras = lir::NoRegister; + } + + int fps; + if (framePointerSurrogate) { + framePointerSurrogate->source->freeze(c, framePointerSurrogate); + + fps = static_cast + (framePointerSurrogate->source)->number; + } else { + fps = lir::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::LongJumpOrCall) { + if (flags & Compiler::Aligned) { + op = lir::AlignedLongCall; + } else { + op = lir::LongCall; + } + } else if (flags & Compiler::Aligned) { + op = lir::AlignedCall; + } else { + op = lir::Call; + } + + apply(c, op, vm::TargetBytesPerWord, address->source, address->source); + + if (traceHandler) { + traceHandler->handleTrace(codePromise(c, c->assembler->offset(true)), + 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); + + if (resultSize and live(c, result)) { + result->addSite(c, registerSite(c, c->arch->returnLow())); + if (resultSize > vm::TargetBytesPerWord and live(c, result->nextWord)) { + result->nextWord->addSite(c, registerSite(c, c->arch->returnHigh())); + } + } + } + + virtual bool allExits() { + return (flags & Compiler::TailJump) != 0; + } + + Value* address; + TraceHandler* traceHandler; + Value* result; + Value* returnAddressSurrogate; + Value* framePointerSurrogate; + unsigned popIndex; + unsigned stackArgumentIndex; + unsigned flags; + unsigned resultSize; + unsigned stackArgumentFootprint; +}; + +void +appendCall(Context* c, Value* address, unsigned flags, + TraceHandler* traceHandler, Value* result, unsigned resultSize, + Stack* argumentStack, unsigned argumentCount, + unsigned stackArgumentFootprint) +{ + append(c, new(c->zone) + CallEvent(c, address, flags, traceHandler, result, + resultSize, argumentStack, argumentCount, + stackArgumentFootprint)); +} + + +class ReturnEvent: public Event { + public: + ReturnEvent(Context* c, unsigned size, Value* value): + Event(c), value(value) + { + if (value) { + this->addReads(c, value, size, + SiteMask::fixedRegisterMask(c->arch->returnLow()), + SiteMask::fixedRegisterMask(c->arch->returnHigh())); + } + } + + virtual const char* name() { + return "ReturnEvent"; + } + + virtual void compile(Context* c) { + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + if (not this->isUnreachable()) { + c->assembler->popFrameAndPopArgumentsAndReturn + (c->alignedFrameSize, + c->arch->argumentFootprint(c->parameterFootprint)); + } + } + + Value* value; +}; + +void appendReturn(Context* c, unsigned size, Value* value) { + append(c, new(c->zone) ReturnEvent(c, size, value)); +} + +class MoveEvent: public Event { + public: + MoveEvent(Context* c, lir::BinaryOperation type, unsigned srcSize, + unsigned srcSelectSize, Value* srcValue, unsigned dstSize, Value* dstValue, + const SiteMask& srcLowMask, const SiteMask& srcHighMask): + Event(c), type(type), srcSize(srcSize), srcSelectSize(srcSelectSize), + srcValue(srcValue), dstSize(dstSize), dstValue(dstValue) + { + assert(c, srcSelectSize <= srcSize); + + bool noop = srcSelectSize >= dstSize; + + if (dstSize > vm::TargetBytesPerWord) { + dstValue->grow(c); + } + + if (srcSelectSize > vm::TargetBytesPerWord) { + srcValue->maybeSplit(c); + } + + this->addReads(c, srcValue, srcSelectSize, srcLowMask, noop ? dstValue : 0, + srcHighMask, + noop and dstSize > vm::TargetBytesPerWord ? dstValue->nextWord : 0); + } + + virtual const char* name() { + return "MoveEvent"; + } + + virtual void compile(Context* c) { + OperandMask dst; + + c->arch->planDestination + (type, + srcSelectSize, + OperandMask( + 1 << srcValue->source->type(c), + (static_cast(srcValue->nextWord->source->registerMask(c)) << 32) + | static_cast(srcValue->source->registerMask(c))), + dstSize, dst); + + SiteMask dstLowMask = SiteMask::lowPart(dst); + SiteMask dstHighMask = SiteMask::highPart(dst); + + if (srcSelectSize >= vm::TargetBytesPerWord + and dstSize >= vm::TargetBytesPerWord + and srcSelectSize >= dstSize) + { + if (dstValue->target) { + if (dstSize > vm::TargetBytesPerWord) { + if (srcValue->source->registerSize(c) > vm::TargetBytesPerWord) { + apply(c, lir::Move, srcSelectSize, srcValue->source, srcValue->source, + dstSize, dstValue->target, dstValue->target); + + if (live(c, dstValue) == 0) { + dstValue->removeSite(c, dstValue->target); + if (dstSize > vm::TargetBytesPerWord) { + dstValue->nextWord->removeSite(c, dstValue->nextWord->target); + } + } + } else { + srcValue->nextWord->source->freeze(c, srcValue->nextWord); + + maybeMove(c, lir::Move, vm::TargetBytesPerWord, vm::TargetBytesPerWord, srcValue, + vm::TargetBytesPerWord, dstValue, dstLowMask); + + srcValue->nextWord->source->thaw(c, srcValue->nextWord); + + maybeMove + (c, lir::Move, vm::TargetBytesPerWord, vm::TargetBytesPerWord, srcValue->nextWord, + vm::TargetBytesPerWord, dstValue->nextWord, dstHighMask); + } + } else { + maybeMove(c, lir::Move, vm::TargetBytesPerWord, vm::TargetBytesPerWord, srcValue, + vm::TargetBytesPerWord, dstValue, dstLowMask); + } + } else { + Site* low = pickSiteOrMove(c, srcValue, dstValue, 0, 0); + if (dstSize > vm::TargetBytesPerWord) { + pickSiteOrMove(c, srcValue->nextWord, dstValue->nextWord, low, 1); + } + } + } else if (srcSelectSize <= vm::TargetBytesPerWord + and dstSize <= vm::TargetBytesPerWord) + { + maybeMove(c, type, srcSize, srcSelectSize, srcValue, dstSize, dstValue, + dstLowMask); + } else { + assert(c, srcSize == vm::TargetBytesPerWord); + assert(c, srcSelectSize == vm::TargetBytesPerWord); + + if (dstValue->nextWord->target or live(c, dstValue->nextWord)) { + assert(c, dstLowMask.typeMask & (1 << lir::RegisterOperand)); + + Site* low = freeRegisterSite(c, dstLowMask.registerMask); + + srcValue->source->freeze(c, srcValue); + + dstValue->addSite(c, low); + + low->freeze(c, dstValue); + + if (DebugMoves) { + char srcb[256]; srcValue->source->toString(c, srcb, 256); + char dstb[256]; low->toString(c, dstb, 256); + fprintf(stderr, "move %s to %s for %p\n", + srcb, dstb, srcValue); + } + + apply(c, lir::Move, vm::TargetBytesPerWord, srcValue->source, srcValue->source, + vm::TargetBytesPerWord, low, low); + + low->thaw(c, dstValue); + + srcValue->source->thaw(c, srcValue); + + assert(c, dstHighMask.typeMask & (1 << lir::RegisterOperand)); + + Site* high = freeRegisterSite(c, dstHighMask.registerMask); + + low->freeze(c, dstValue); + + dstValue->nextWord->addSite(c, high); + + high->freeze(c, dstValue->nextWord); + + if (DebugMoves) { + char srcb[256]; low->toString(c, srcb, 256); + char dstb[256]; high->toString(c, dstb, 256); + fprintf(stderr, "extend %s to %s for %p %p\n", + srcb, dstb, dstValue, dstValue->nextWord); + } + + apply(c, lir::Move, vm::TargetBytesPerWord, low, low, dstSize, low, high); + + high->thaw(c, dstValue->nextWord); + + low->thaw(c, dstValue); + } else { + pickSiteOrMove(c, srcValue, dstValue, 0, 0); + } + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } + + lir::BinaryOperation type; + unsigned srcSize; + unsigned srcSelectSize; + Value* srcValue; + unsigned dstSize; + Value* dstValue; +}; + +void +appendMove(Context* c, lir::BinaryOperation type, unsigned srcSize, + unsigned srcSelectSize, Value* srcValue, unsigned dstSize, Value* dstValue) +{ + bool thunk; + OperandMask src; + + c->arch->planSource + (type, srcSelectSize, src, dstSize, &thunk); + + assert(c, not thunk); + + append(c, new(c->zone) + MoveEvent + (c, type, srcSize, srcSelectSize, srcValue, dstSize, dstValue, + SiteMask::lowPart(src), + SiteMask::highPart(src))); +} + + +void +freezeSource(Context* c, unsigned size, Value* v) +{ + v->source->freeze(c, v); + if (size > vm::TargetBytesPerWord) { + v->nextWord->source->freeze(c, v->nextWord); + } +} + +void +thawSource(Context* c, unsigned size, Value* v) +{ + v->source->thaw(c, v); + if (size > vm::TargetBytesPerWord) { + v->nextWord->source->thaw(c, v->nextWord); + } +} + +Read* liveNext(Context* c, Value* v) { + assert(c, v->buddy->hasBuddy(c, v)); + + Read* r = v->reads->next(c); + if (valid(r)) return r; + + for (Value* p = v->buddy; p != v; p = p->buddy) { + if (valid(p->reads)) return p->reads; + } + + return 0; +} + +void preserve(Context* c, Value* v, Read* r, Site* s) { + s->freeze(c, v); + + maybeMove(c, r, false, true, 0); + + s->thaw(c, v); +} + +Site* getTarget(Context* c, Value* value, Value* result, const SiteMask& resultMask) { + Site* s; + Value* v; + Read* r = liveNext(c, value); + if (value->source->match + (c, static_cast(resultMask)) + and (r == 0 or value->source->loneMatch + (c, static_cast(resultMask)))) + { + s = value->source; + v = value; + if (r and v->uniqueSite(c, s)) { + preserve(c, v, r, s); + } + } else { + SingleRead r(resultMask, 0); + r.value = result; + r.successor_ = result; + s = pickTargetSite(c, &r, true); + v = result; + result->addSite(c, s); + } + + v->removeSite(c, s); + + s->freeze(c, v); + + return s; +} + +class CombineEvent: public Event { + public: + CombineEvent(Context* c, lir::TernaryOperation type, + unsigned firstSize, Value* firstValue, + unsigned secondSize, Value* secondValue, + unsigned resultSize, Value* resultValue, + const SiteMask& firstLowMask, + const SiteMask& firstHighMask, + const SiteMask& secondLowMask, + const SiteMask& secondHighMask): + Event(c), type(type), firstSize(firstSize), firstValue(firstValue), + secondSize(secondSize), secondValue(secondValue), resultSize(resultSize), + resultValue(resultValue) + { + this->addReads(c, firstValue, firstSize, firstLowMask, firstHighMask); + + if (resultSize > vm::TargetBytesPerWord) { + resultValue->grow(c); + } + + bool condensed = c->arch->alwaysCondensed(type); + + this->addReads(c, secondValue, secondSize, + secondLowMask, condensed ? resultValue : 0, + secondHighMask, condensed ? resultValue->nextWord : 0); + } + + virtual const char* name() { + return "CombineEvent"; + } + + virtual void compile(Context* c) { + assert(c, firstValue->source->type(c) == firstValue->nextWord->source->type(c)); + + // if (secondValue->source->type(c) != secondValue->nextWord->source->type(c)) { + // fprintf(stderr, "%p %p %d : %p %p %d\n", + // secondValue, secondValue->source, secondValue->source->type(c), + // secondValue->nextWord, secondValue->nextWord->source, + // secondValue->nextWord->source->type(c)); + // } + + assert(c, secondValue->source->type(c) == secondValue->nextWord->source->type(c)); + + freezeSource(c, firstSize, firstValue); + + OperandMask cMask; + + c->arch->planDestination + (type, + firstSize, + OperandMask( + 1 << firstValue->source->type(c), + (static_cast(firstValue->nextWord->source->registerMask(c)) << 32) + | static_cast(firstValue->source->registerMask(c))), + secondSize, + OperandMask( + 1 << secondValue->source->type(c), + (static_cast(secondValue->nextWord->source->registerMask(c)) << 32) + | static_cast(secondValue->source->registerMask(c))), + resultSize, + cMask); + + SiteMask resultLowMask = SiteMask::lowPart(cMask); + SiteMask resultHighMask = SiteMask::highPart(cMask); + + Site* low = getTarget(c, secondValue, resultValue, resultLowMask); + unsigned lowSize = low->registerSize(c); + Site* high + = (resultSize > lowSize + ? getTarget(c, secondValue->nextWord, resultValue->nextWord, resultHighMask) + : low); + +// fprintf(stderr, "combine %p:%p and %p:%p into %p:%p\n", +// firstValue, firstValue->nextWord, +// secondValue, secondValue->nextWord, +// resultValue, resultValue->nextWord); + + apply(c, type, + firstSize, firstValue->source, firstValue->nextWord->source, + secondSize, secondValue->source, secondValue->nextWord->source, + resultSize, low, high); + + thawSource(c, firstSize, firstValue); + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + low->thaw(c, secondValue); + if (resultSize > lowSize) { + high->thaw(c, secondValue->nextWord); + } + + if (live(c, resultValue)) { + resultValue->addSite(c, low); + if (resultSize > lowSize and live(c, resultValue->nextWord)) { + resultValue->nextWord->addSite(c, high); + } + } + } + + lir::TernaryOperation type; + unsigned firstSize; + Value* firstValue; + unsigned secondSize; + Value* secondValue; + unsigned resultSize; + Value* resultValue; +}; + +void +appendCombine(Context* c, lir::TernaryOperation type, + unsigned firstSize, Value* firstValue, + unsigned secondSize, Value* secondValue, + unsigned resultSize, Value* resultValue) +{ + bool thunk; + OperandMask firstMask; + OperandMask secondMask; + + c->arch->planSource(type, + firstSize, firstMask, + secondSize, secondMask, + resultSize, + &thunk); + + if (thunk) { + Stack* oldStack = c->stack; + + bool threadParameter; + intptr_t handler = c->client->getThunk + (type, firstSize, resultSize, &threadParameter); + + unsigned stackSize = ceilingDivide(secondSize, vm::TargetBytesPerWord) + + ceilingDivide(firstSize, vm::TargetBytesPerWord); + + compiler::push(c, ceilingDivide(secondSize, vm::TargetBytesPerWord), secondValue); + compiler::push(c, ceilingDivide(firstSize, vm::TargetBytesPerWord), firstValue); + + if (threadParameter) { + ++ stackSize; + + compiler::push(c, 1, register_(c, c->arch->thread())); + } + + Stack* argumentStack = c->stack; + c->stack = oldStack; + + appendCall + (c, value(c, lir::ValueGeneral, constantSite(c, handler)), 0, 0, resultValue, + resultSize, argumentStack, stackSize, 0); + } else { + append + (c, new(c->zone) + CombineEvent + (c, type, + firstSize, firstValue, + secondSize, secondValue, + resultSize, resultValue, + SiteMask::lowPart(firstMask), + SiteMask::highPart(firstMask), + SiteMask::lowPart(secondMask), + SiteMask::highPart(secondMask))); + } +} + +class TranslateEvent: public Event { + public: + TranslateEvent(Context* c, lir::BinaryOperation type, unsigned valueSize, + Value* value, unsigned resultSize, Value* resultValue, + const SiteMask& valueLowMask, + const SiteMask& valueHighMask): + Event(c), type(type), valueSize(valueSize), resultSize(resultSize), + value(value), resultValue(resultValue) + { + bool condensed = c->arch->alwaysCondensed(type); + + if (resultSize > vm::TargetBytesPerWord) { + resultValue->grow(c); + } + + this->addReads(c, value, valueSize, valueLowMask, condensed ? resultValue : 0, + valueHighMask, condensed ? resultValue->nextWord : 0); + } + + virtual const char* name() { + return "TranslateEvent"; + } + + virtual void compile(Context* c) { + assert(c, value->source->type(c) == value->nextWord->source->type(c)); + + OperandMask bMask; + + c->arch->planDestination + (type, + valueSize, + OperandMask( + 1 << value->source->type(c), + (static_cast(value->nextWord->source->registerMask(c)) << 32) + | static_cast(value->source->registerMask(c))), + resultSize, + bMask); + + SiteMask resultLowMask = SiteMask::lowPart(bMask); + SiteMask resultHighMask = SiteMask::highPart(bMask); + + Site* low = getTarget(c, value, resultValue, resultLowMask); + unsigned lowSize = low->registerSize(c); + Site* high + = (resultSize > lowSize + ? getTarget(c, value->nextWord, resultValue->nextWord, resultHighMask) + : low); + + apply(c, type, valueSize, value->source, value->nextWord->source, + resultSize, low, high); + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + low->thaw(c, value); + if (resultSize > lowSize) { + high->thaw(c, value->nextWord); + } + + if (live(c, resultValue)) { + resultValue->addSite(c, low); + if (resultSize > lowSize and live(c, resultValue->nextWord)) { + resultValue->nextWord->addSite(c, high); + } + } + } + + lir::BinaryOperation type; + unsigned valueSize; + unsigned resultSize; + Value* value; + Value* resultValue; + Read* resultRead; + SiteMask resultLowMask; + SiteMask resultHighMask; +}; + +void +appendTranslate(Context* c, lir::BinaryOperation type, unsigned firstSize, + Value* firstValue, unsigned resultSize, Value* resultValue) +{ + bool thunk; + OperandMask first; + + c->arch->planSource(type, firstSize, first, + resultSize, &thunk); + + if (thunk) { + Stack* oldStack = c->stack; + + compiler::push(c, ceilingDivide(firstSize, vm::TargetBytesPerWord), firstValue); + + Stack* argumentStack = c->stack; + c->stack = oldStack; + + appendCall + (c, value + (c, lir::ValueGeneral, constantSite + (c, c->client->getThunk(type, firstSize, resultSize))), + 0, 0, resultValue, resultSize, argumentStack, + ceilingDivide(firstSize, vm::TargetBytesPerWord), 0); + } else { + append(c, new(c->zone) + TranslateEvent + (c, type, firstSize, firstValue, resultSize, resultValue, + SiteMask::lowPart(first), + SiteMask::highPart(first))); + } +} + +class OperationEvent: public Event { + public: + OperationEvent(Context* c, lir::Operation op): + Event(c), op(op) + { } + + virtual const char* name() { + return "OperationEvent"; + } + + virtual void compile(Context* c) { + c->assembler->apply(op); + } + + lir::Operation op; +}; + +void +appendOperation(Context* c, lir::Operation op) +{ + append(c, new(c->zone) OperationEvent(c, op)); +} + +ConstantSite* findConstantSite(Context* c, Value* v) { + for (SiteIterator it(c, v); it.hasMore();) { + Site* s = it.next(); + if (s->type(c) == lir::ConstantOperand) { + return static_cast(s); + } + } + return 0; +} + +void +moveIfConflict(Context* c, Value* v, MemorySite* s) +{ + if (v->reads) { + SiteMask mask(1 << lir::RegisterOperand, ~0, AnyFrameIndex); + v->reads->intersect(&mask); + if (s->conflicts(mask)) { + maybeMove(c, v->reads, true, false); + v->removeSite(c, s); + } + } +} + +class MemoryEvent: public Event { + public: + MemoryEvent(Context* c, Value* base, int displacement, Value* index, + unsigned scale, Value* result): + Event(c), base(base), displacement(displacement), index(index), + scale(scale), result(result) + { + this->addRead(c, base, generalRegisterMask(c)); + if (index) { + this->addRead(c, index, generalRegisterOrConstantMask(c)); + } + } + + virtual const char* name() { + return "MemoryEvent"; + } + + virtual void compile(Context* c) { + int indexRegister; + int displacement = this->displacement; + unsigned scale = this->scale; + if (index) { + ConstantSite* constant = findConstantSite(c, index); + + if (constant) { + indexRegister = lir::NoRegister; + displacement += (constant->value->value() * scale); + scale = 1; + } else { + assert(c, index->source->type(c) == lir::RegisterOperand); + indexRegister = static_cast(index->source)->number; + } + } else { + indexRegister = lir::NoRegister; + } + assert(c, base->source->type(c) == lir::RegisterOperand); + int baseRegister = static_cast(base->source)->number; + + popRead(c, this, base); + if (index) { + if (vm::TargetBytesPerWord == 8 and indexRegister != lir::NoRegister) { + apply(c, lir::Move, 4, index->source, index->source, + 8, index->source, index->source); + } + + popRead(c, this, index); + } + + MemorySite* site = memorySite + (c, baseRegister, displacement, indexRegister, scale); + + MemorySite* low; + if (result->nextWord != result) { + MemorySite* high = static_cast(site->copyHigh(c)); + low = static_cast(site->copyLow(c)); + + result->nextWord->target = high; + result->nextWord->addSite(c, high); + moveIfConflict(c, result->nextWord, high); + } else { + low = site; + } + + result->target = low; + result->addSite(c, low); + moveIfConflict(c, result, low); + } + + Value* base; + int displacement; + Value* index; + unsigned scale; + Value* result; +}; + +void +appendMemory(Context* c, Value* base, int displacement, Value* index, + unsigned scale, Value* result) +{ + append(c, new(c->zone) + MemoryEvent(c, base, displacement, index, scale, result)); +} + +double asFloat(unsigned size, int64_t v) { + if (size == 4) { + return vm::bitsToFloat(v); + } else { + return vm::bitsToDouble(v); + } +} + +bool +unordered(double a, double b) +{ + return not (a >= b or a < b); +} + +bool +shouldJump(Context* c, lir::TernaryOperation type, unsigned size, int64_t b, + int64_t a) +{ + switch (type) { + case lir::JumpIfEqual: + return a == b; + + case lir::JumpIfNotEqual: + return a != b; + + case lir::JumpIfLess: + return a < b; + + case lir::JumpIfGreater: + return a > b; + + case lir::JumpIfLessOrEqual: + return a <= b; + + case lir::JumpIfGreaterOrEqual: + return a >= b; + + case lir::JumpIfFloatEqual: + return asFloat(size, a) == asFloat(size, b); + + case lir::JumpIfFloatNotEqual: + return asFloat(size, a) != asFloat(size, b); + + case lir::JumpIfFloatLess: + return asFloat(size, a) < asFloat(size, b); + + case lir::JumpIfFloatGreater: + return asFloat(size, a) > asFloat(size, b); + + case lir::JumpIfFloatLessOrEqual: + return asFloat(size, a) <= asFloat(size, b); + + case lir::JumpIfFloatGreaterOrEqual: + return asFloat(size, a) >= asFloat(size, b); + + case lir::JumpIfFloatLessOrUnordered: + return asFloat(size, a) < asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatGreaterOrUnordered: + return asFloat(size, a) > asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatLessOrEqualOrUnordered: + return asFloat(size, a) <= asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return asFloat(size, a) >= asFloat(size, b) + or unordered(asFloat(size, a), asFloat(size, b)); + + default: + abort(c); + } +} + +lir::TernaryOperation +thunkBranch(Context* c, lir::TernaryOperation type) +{ + switch (type) { + case lir::JumpIfFloatEqual: + return lir::JumpIfEqual; + + case lir::JumpIfFloatNotEqual: + return lir::JumpIfNotEqual; + + case lir::JumpIfFloatLess: + case lir::JumpIfFloatLessOrUnordered: + return lir::JumpIfLess; + + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatGreaterOrUnordered: + return lir::JumpIfGreater; + + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatLessOrEqualOrUnordered: + return lir::JumpIfLessOrEqual; + + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return lir::JumpIfGreaterOrEqual; + + default: + abort(c); + } +} + +class BranchEvent: public Event { + public: + BranchEvent(Context* c, lir::TernaryOperation type, unsigned size, + Value* firstValue, Value* secondValue, Value* addressValue, + const SiteMask& firstLowMask, + const SiteMask& firstHighMask, + const SiteMask& secondLowMask, + const SiteMask& secondHighMask): + Event(c), type(type), size(size), firstValue(firstValue), secondValue(secondValue), + addressValue(addressValue) + { + this->addReads(c, firstValue, size, firstLowMask, firstHighMask); + this->addReads(c, secondValue, size, secondLowMask, secondHighMask); + + OperandMask dstMask; + c->arch->planDestination(type, + size, OperandMask(0, 0), + size, OperandMask(0, 0), + vm::TargetBytesPerWord, dstMask); + + this->addRead(c, addressValue, SiteMask::lowPart(dstMask)); + } + + virtual const char* name() { + return "BranchEvent"; + } + + virtual void compile(Context* c) { + ConstantSite* firstConstant = findConstantSite(c, firstValue); + ConstantSite* secondConstant = findConstantSite(c, secondValue); + + if (not this->isUnreachable()) { + if (firstConstant + and secondConstant + and firstConstant->value->resolved() + and secondConstant->value->resolved()) + { + int64_t firstConstVal = firstConstant->value->value(); + int64_t secondConstVal = secondConstant->value->value(); + + if (size > vm::TargetBytesPerWord) { + firstConstVal |= findConstantSite + (c, firstValue->nextWord)->value->value() << 32; + secondConstVal |= findConstantSite + (c, secondValue->nextWord)->value->value() << 32; + } + + if (shouldJump(c, type, size, firstConstVal, secondConstVal)) { + apply(c, lir::Jump, vm::TargetBytesPerWord, addressValue->source, addressValue->source); + } + } else { + freezeSource(c, size, firstValue); + freezeSource(c, size, secondValue); + freezeSource(c, vm::TargetBytesPerWord, addressValue); + + apply(c, type, size, firstValue->source, firstValue->nextWord->source, + size, secondValue->source, secondValue->nextWord->source, + vm::TargetBytesPerWord, addressValue->source, addressValue->source); + + thawSource(c, vm::TargetBytesPerWord, addressValue); + thawSource(c, size, secondValue); + thawSource(c, size, firstValue); + } + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } + + virtual bool isBranch() { return true; } + + lir::TernaryOperation type; + unsigned size; + Value* firstValue; + Value* secondValue; + Value* addressValue; +}; + +void +appendBranch(Context* c, lir::TernaryOperation type, unsigned size, Value* firstValue, + Value* secondValue, Value* addressValue) +{ + bool thunk; + OperandMask firstMask; + OperandMask secondMask; + + c->arch->planSource(type, + size, firstMask, + size, secondMask, + vm::TargetBytesPerWord, &thunk); + + if (thunk) { + Stack* oldStack = c->stack; + + bool threadParameter; + intptr_t handler = c->client->getThunk + (type, size, size, &threadParameter); + + assert(c, not threadParameter); + + compiler::push(c, ceilingDivide(size, vm::TargetBytesPerWord), secondValue); + compiler::push(c, ceilingDivide(size, vm::TargetBytesPerWord), firstValue); + + Stack* argumentStack = c->stack; + c->stack = oldStack; + + Value* result = value(c, lir::ValueGeneral); + appendCall + (c, value + (c, lir::ValueGeneral, constantSite(c, handler)), 0, 0, result, 4, + argumentStack, ceilingDivide(size, vm::TargetBytesPerWord) * 2, 0); + + appendBranch(c, thunkBranch(c, type), 4, value + (c, lir::ValueGeneral, constantSite(c, static_cast(0))), + result, addressValue); + } else { + append + (c, new(c->zone) + BranchEvent + (c, type, size, firstValue, secondValue, addressValue, + SiteMask::lowPart(firstMask), + SiteMask::highPart(firstMask), + SiteMask::lowPart(secondMask), + SiteMask::highPart(secondMask))); + } +} + +void clean(Context* c, Value* v, unsigned popIndex) { + for (SiteIterator it(c, v); it.hasMore();) { + Site* s = it.next(); + if (not (s->match(c, SiteMask(1 << lir::MemoryOperand, 0, AnyFrameIndex)) + and offsetToFrameIndex + (c, static_cast(s)->offset) + >= popIndex)) + { + if (false and + s->match(c, SiteMask(1 << lir::MemoryOperand, 0, AnyFrameIndex))) + { + char buffer[256]; s->toString(c, buffer, 256); + fprintf(stderr, "remove %s from %p at %d pop offset 0x%x\n", + buffer, v, offsetToFrameIndex + (c, static_cast(s)->offset), + frameIndexToOffset(c, popIndex)); + } + it.remove(c); + } + } +} + +void +clean(Context* c, Event* e, Stack* stack, Local* locals, Read* reads, + unsigned popIndex) +{ + for (FrameIterator it(c, stack, locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, popIndex); + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, e, r->value); + } +} + +class JumpEvent: public Event { + public: + JumpEvent(Context* c, lir::UnaryOperation type, Value* address, bool exit, + bool cleanLocals): + Event(c), type(type), address(address), exit(exit), + cleanLocals(cleanLocals) + { + bool thunk; + OperandMask mask; + c->arch->plan(type, vm::TargetBytesPerWord, mask, &thunk); + + assert(c, not thunk); + + this->addRead(c, address, SiteMask::lowPart(mask)); + } + + virtual const char* name() { + return "JumpEvent"; + } + + virtual void compile(Context* c) { + if (not this->isUnreachable()) { + apply(c, type, vm::TargetBytesPerWord, address->source, address->source); + } + + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + + if (cleanLocals) { + for (FrameIterator it(c, 0, c->locals); it.hasMore();) { + FrameIterator::Element e = it.next(c); + clean(c, e.value, 0); + } + } + } + + virtual bool isBranch() { return true; } + + virtual bool allExits() { + return exit or this->isUnreachable(); + } + + lir::UnaryOperation type; + Value* address; + bool exit; + bool cleanLocals; +}; + +void appendJump(Context* c, lir::UnaryOperation type, Value* address, bool exit, bool cleanLocals) { + append(c, new(c->zone) JumpEvent(c, type, address, exit, cleanLocals)); +} + +class BoundsCheckEvent: public Event { + public: + BoundsCheckEvent(Context* c, Value* object, unsigned lengthOffset, + Value* index, intptr_t handler): + Event(c), object(object), lengthOffset(lengthOffset), index(index), + handler(handler) + { + this->addRead(c, object, generalRegisterMask(c)); + this->addRead(c, index, generalRegisterOrConstantMask(c)); + } + + virtual const char* name() { + return "BoundsCheckEvent"; + } + + virtual void compile(Context* c) { + Assembler* a = c->assembler; + + ConstantSite* constant = findConstantSite(c, index); + CodePromise* outOfBoundsPromise = 0; + + if (constant) { + if (constant->value->value() < 0) { + lir::Constant handlerConstant(resolvedPromise(c, handler)); + a->apply(lir::Call, + OperandInfo(vm::TargetBytesPerWord, lir::ConstantOperand, &handlerConstant)); + } + } else { + outOfBoundsPromise = compiler::codePromise(c, static_cast(0)); + + ConstantSite zero(resolvedPromise(c, 0)); + ConstantSite oob(outOfBoundsPromise); + apply(c, lir::JumpIfLess, + 4, &zero, &zero, + 4, index->source, index->source, + vm::TargetBytesPerWord, &oob, &oob); + } + + if (constant == 0 or constant->value->value() >= 0) { + assert(c, object->source->type(c) == lir::RegisterOperand); + MemorySite length(static_cast(object->source)->number, + lengthOffset, lir::NoRegister, 1); + length.acquired = true; + + CodePromise* nextPromise = compiler::codePromise(c, static_cast(0)); + + freezeSource(c, vm::TargetBytesPerWord, index); + + ConstantSite next(nextPromise); + apply(c, lir::JumpIfGreater, + 4, index->source, + index->source, 4, &length, + &length, vm::TargetBytesPerWord, &next, &next); + + thawSource(c, vm::TargetBytesPerWord, index); + + if (constant == 0) { + outOfBoundsPromise->offset = a->offset(); + } + + lir::Constant handlerConstant(resolvedPromise(c, handler)); + a->apply(lir::Call, + OperandInfo(vm::TargetBytesPerWord, lir::ConstantOperand, &handlerConstant)); + + nextPromise->offset = a->offset(); + } + + popRead(c, this, object); + popRead(c, this, index); + } + + Value* object; + unsigned lengthOffset; + Value* index; + intptr_t handler; +}; + +void +appendBoundsCheck(Context* c, Value* object, unsigned lengthOffset, + Value* index, intptr_t handler) +{ + append(c, new(c->zone) BoundsCheckEvent(c, object, lengthOffset, index, handler)); +} + + +class FrameSiteEvent: public Event { + public: + FrameSiteEvent(Context* c, Value* value, int index): + Event(c), value(value), index(index) + { } + + virtual const char* name() { + return "FrameSiteEvent"; + } + + virtual void compile(Context* c) { + if (live(c, value)) { + value->addSite(c, frameSite(c, index)); + } + } + + Value* value; + int index; +}; + +void +appendFrameSite(Context* c, Value* value, int index) +{ + append(c, new(c->zone) FrameSiteEvent(c, value, index)); +} + +class SaveLocalsEvent: public Event { + public: + SaveLocalsEvent(Context* c): + Event(c) + { + saveLocals(c, this); + } + + virtual const char* name() { + return "SaveLocalsEvent"; + } + + virtual void compile(Context* c) { + for (Read* r = reads; r; r = r->eventNext) { + popRead(c, this, r->value); + } + } +}; + +void +appendSaveLocals(Context* c) +{ + append(c, new(c->zone) SaveLocalsEvent(c)); +} + +class DummyEvent: public Event { + public: + DummyEvent(Context* c, Local* locals): + Event(c), + locals_(locals) + { } + + virtual const char* name() { + return "DummyEvent"; + } + + virtual void compile(Context*) { } + + virtual Local* locals() { + return locals_; + } + + Local* locals_; +}; + +void +appendDummy(Context* c) +{ + Stack* stack = c->stack; + Local* locals = c->locals; + LogicalInstruction* i = c->logicalCode[c->logicalIp]; + + c->stack = i->stack; + c->locals = i->locals; + + append(c, new(c->zone) DummyEvent(c, locals)); + + c->stack = stack; + c->locals = locals; +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/event.h b/src/codegen/compiler/event.h new file mode 100644 index 0000000000..504da33bae --- /dev/null +++ b/src/codegen/compiler/event.h @@ -0,0 +1,171 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_EVENT_H +#define AVIAN_CODEGEN_COMPILER_EVENT_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class CodePromise; +class Snapshot; +class Link; +class Site; +class StubRead; + +const bool DebugReads = false; +const bool DebugMoves = false; + +class Event { + public: + Event(Context* c); + + virtual const char* name() = 0; + + virtual void compile(Context* c) = 0; + + virtual bool isBranch() { return false; } + + virtual bool allExits() { return false; } + + virtual Local* locals() { return localsBefore; } + + + + void addRead(Context* c, Value* v, Read* r); + + void addRead(Context* c, Value* v, const SiteMask& mask, + Value* successor = 0); + + void addReads(Context* c, Value* v, unsigned size, + const SiteMask& lowMask, Value* lowSuccessor, + const SiteMask& highMask, Value* highSuccessor); + + void addReads(Context* c, Value* v, unsigned size, + const SiteMask& lowMask, const SiteMask& highMask); + + CodePromise* makeCodePromise(Context* c); + + bool isUnreachable(); + + Event* next; + Stack* stackBefore; + Local* localsBefore; + Stack* stackAfter; + Local* localsAfter; + CodePromise* promises; + Read* reads; + Site** junctionSites; + Snapshot* snapshots; + Link* predecessors; + Link* successors; + Cell* visitLinks; + Block* block; + LogicalInstruction* logicalInstruction; + unsigned readCount; +}; + +class StubReadPair { + public: + Value* value; + StubRead* read; +}; + +class JunctionState { + public: + JunctionState(unsigned frameFootprint): frameFootprint(frameFootprint) { } + + unsigned frameFootprint; + StubReadPair reads[0]; +}; + +class Link { + public: + Link(Event* predecessor, Link* nextPredecessor, Event* successor, + Link* nextSuccessor, ForkState* forkState): + predecessor(predecessor), nextPredecessor(nextPredecessor), + successor(successor), nextSuccessor(nextSuccessor), forkState(forkState), + junctionState(0) + { } + + unsigned countPredecessors(); + Link* lastPredecessor(); + unsigned countSuccessors(); + + Event* predecessor; + Link* nextPredecessor; + Event* successor; + Link* nextSuccessor; + ForkState* forkState; + JunctionState* junctionState; +}; + +Link* +link(Context* c, Event* predecessor, Link* nextPredecessor, Event* successor, + Link* nextSuccessor, ForkState* forkState); + +void +appendCall(Context* c, Value* address, unsigned flags, + TraceHandler* traceHandler, Value* result, unsigned resultSize, + Stack* argumentStack, unsigned argumentCount, + unsigned stackArgumentFootprint); + +void +appendReturn(Context* c, unsigned size, Value* value); + +void +appendMove(Context* c, lir::BinaryOperation type, unsigned srcSize, + unsigned srcSelectSize, Value* src, unsigned dstSize, Value* dst); + +void +appendCombine(Context* c, lir::TernaryOperation type, + unsigned firstSize, Value* first, + unsigned secondSize, Value* second, + unsigned resultSize, Value* result); + +void +appendTranslate(Context* c, lir::BinaryOperation type, unsigned firstSize, + Value* first, unsigned resultSize, Value* result); + +void +appendOperation(Context* c, lir::Operation op); + +void +appendMemory(Context* c, Value* base, int displacement, Value* index, + unsigned scale, Value* result); + +void +appendBranch(Context* c, lir::TernaryOperation type, unsigned size, Value* first, + Value* second, Value* address); + +void +appendJump(Context* c, lir::UnaryOperation type, Value* address, bool exit = false, + bool cleanLocals = false); + +void +appendBoundsCheck(Context* c, Value* object, unsigned lengthOffset, + Value* index, intptr_t handler); + +void +appendFrameSite(Context* c, Value* value, int index); + +void +appendSaveLocals(Context* c); + +void +appendDummy(Context* c); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_EVENT_H diff --git a/src/codegen/compiler/frame.cpp b/src/codegen/compiler/frame.cpp new file mode 100644 index 0000000000..6f73300d82 --- /dev/null +++ b/src/codegen/compiler/frame.cpp @@ -0,0 +1,121 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/frame.h" + +#include + +namespace avian { +namespace codegen { +namespace compiler { + +unsigned totalFrameSize(Context* c) { + return c->alignedFrameSize + + c->arch->frameHeaderSize() + + c->arch->argumentFootprint(c->parameterFootprint); +} + +int frameIndex(Context* c, int localIndex) { + assert(c, localIndex >= 0); + + 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) { + assert(c, frameIndex < totalFrameSize(c)); + + return (frameIndex + c->arch->frameFooterSize()) * vm::TargetBytesPerWord; +} + +unsigned offsetToFrameIndex(Context* c, unsigned offset) { + assert(c, static_cast + ((offset / vm::TargetBytesPerWord) - c->arch->frameFooterSize()) >= 0); + assert(c, ((offset / vm::TargetBytesPerWord) - c->arch->frameFooterSize()) + < totalFrameSize(c)); + + return (offset / vm::TargetBytesPerWord) - c->arch->frameFooterSize(); +} + +unsigned frameBase(Context* c) { + return c->alignedFrameSize + - c->arch->frameReturnAddressSize() + - c->arch->frameFooterSize() + + c->arch->frameHeaderSize(); +} + +FrameIterator::Element::Element(Value* value, unsigned localIndex): + value(value), localIndex(localIndex) +{ } + + +int FrameIterator::Element::frameIndex(Context* c) { + return compiler::frameIndex(c, this->localIndex); +} + +FrameIterator::FrameIterator(Context* c, Stack* stack, Local* locals, + bool includeEmpty): + stack(stack), locals(locals), localIndex(c->localFootprint - 1), + includeEmpty(includeEmpty) +{ } + +bool FrameIterator::hasMore() { + if (not includeEmpty) { + while (stack and stack->value == 0) { + stack = stack->next; + } + + while (localIndex >= 0 and locals[localIndex].value == 0) { + -- localIndex; + } + } + + return stack != 0 or localIndex >= 0; +} + +FrameIterator::Element FrameIterator::next(Context* c) { + Value* v; + unsigned li; + if (stack) { + Stack* s = stack; + v = s->value; + li = s->index + c->localFootprint; + stack = stack->next; + } else { + Local* l = locals + localIndex; + v = l->value; + li = localIndex; + -- localIndex; + } + return Element(v, li); +} + +Stack* stack(Context* c, Value* value, Stack* next) { + return new(c->zone) Stack(next ? next->index + 1 : 0, value, next); +} + + + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/frame.h b/src/codegen/compiler/frame.h new file mode 100644 index 0000000000..3ae47dfae9 --- /dev/null +++ b/src/codegen/compiler/frame.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_FRAME_H +#define AVIAN_CODEGEN_COMPILER_FRAME_H + +namespace avian { +namespace codegen { +namespace compiler { + +unsigned totalFrameSize(Context* c); + +int frameIndex(Context* c, int localIndex); + +unsigned frameIndexToOffset(Context* c, unsigned frameIndex); + +unsigned offsetToFrameIndex(Context* c, unsigned offset); + +unsigned frameBase(Context* c); + +class FrameIterator { + public: + class Element { + public: + Element(Value* value, unsigned localIndex); + + int frameIndex(Context* c); + + Value* const value; + const unsigned localIndex; + }; + + FrameIterator(Context* c, Stack* stack, Local* locals, + bool includeEmpty = false); + + bool hasMore(); + + Element next(Context* c); + + Stack* stack; + Local* locals; + int localIndex; + bool includeEmpty; +}; + +class Local { + public: + Value* value; +}; + +class Stack { + public: + Stack(unsigned index, Value* value, Stack* next): + index(index), value(value), next(next) + { } + + unsigned index; + Value* value; + Stack* next; +}; + +Stack* stack(Context* c, Value* value, Stack* next); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_FRAME_H diff --git a/src/codegen/compiler/ir.cpp b/src/codegen/compiler/ir.cpp new file mode 100644 index 0000000000..671fd86df8 --- /dev/null +++ b/src/codegen/compiler/ir.cpp @@ -0,0 +1,48 @@ +/* Copyright (c) 2008-2012, 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 "codegen/compiler/context.h" +#include "codegen/compiler/ir.h" + +namespace avian { +namespace codegen { +namespace compiler { + +LogicalInstruction::LogicalInstruction(int index, Stack* stack, Local* locals): + firstEvent(0), lastEvent(0), immediatePredecessor(0), stack(stack), + locals(locals), machineOffset(0), subroutine(0), index(index) +{ } + +LogicalInstruction* LogicalInstruction::next(Context* c) { + LogicalInstruction* i = this; + for (unsigned n = i->index + 1; n < c->logicalCodeLength; ++n) { + i = c->logicalCode[n]; + if (i) return i; + } + return 0; +} + +unsigned +machineOffset(Context* c, int logicalIp) +{ + return c->logicalCode[logicalIp]->machineOffset->value(); +} + +Block::Block(Event* head): + head(head), nextBlock(0), nextInstruction(0), assemblerBlock(0), start(0) +{ } + +Block* block(Context* c, Event* head) { + return new(c->zone) Block(head); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/ir.h b/src/codegen/compiler/ir.h new file mode 100644 index 0000000000..56a0283367 --- /dev/null +++ b/src/codegen/compiler/ir.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_IR_H +#define AVIAN_CODEGEN_COMPILER_IR_H + +namespace avian { +namespace codegen { +namespace compiler { + +class MultiRead; + +class ForkElement { + public: + Value* value; + MultiRead* read; + bool local; +}; + +class ForkState: public Compiler::State { + public: + ForkState(Stack* stack, Local* locals, Cell* saved, Event* predecessor, + unsigned logicalIp): + stack(stack), + locals(locals), + saved(saved), + predecessor(predecessor), + logicalIp(logicalIp), + readCount(0) + { } + + Stack* stack; + Local* locals; + Cell* saved; + Event* predecessor; + unsigned logicalIp; + unsigned readCount; + ForkElement elements[0]; +}; + +class LogicalInstruction { + public: + LogicalInstruction(int index, Stack* stack, Local* locals); + + LogicalInstruction* next(Context* c); + + Event* firstEvent; + Event* lastEvent; + LogicalInstruction* immediatePredecessor; + Stack* stack; + Local* locals; + Promise* machineOffset; + MySubroutine* subroutine; + int index; +}; + +class MySubroutine: public Compiler::Subroutine { + public: + MySubroutine(): forkState(0) { } + + ForkState* forkState; +}; + +class Block { + public: + Block(Event* head); + + Event* head; + Block* nextBlock; + LogicalInstruction* nextInstruction; + Assembler::Block* assemblerBlock; + unsigned start; +}; + +Block* block(Context* c, Event* head); + +unsigned machineOffset(Context* c, int logicalIp); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_IR_H diff --git a/src/codegen/compiler/promise.cpp b/src/codegen/compiler/promise.cpp new file mode 100644 index 0000000000..4363025a4c --- /dev/null +++ b/src/codegen/compiler/promise.cpp @@ -0,0 +1,116 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/promise.h" +#include "codegen/compiler/ir.h" + +namespace avian { +namespace codegen { +namespace compiler { + +CodePromise::CodePromise(Context* c, CodePromise* next): + c(c), offset(0), next(next) +{ } + +CodePromise::CodePromise(Context* c, Promise* offset): + c(c), offset(offset), next(0) +{ } + +int64_t CodePromise::value() { + if (resolved()) { + return reinterpret_cast(c->machineCode + offset->value()); + } + + abort(c); +} + +bool CodePromise::resolved() { + return c->machineCode != 0 and offset and offset->resolved(); +} + +CodePromise* codePromise(Context* c, Promise* offset) { + return new (c->zone) CodePromise(c, offset); +} + +Promise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) { + return new (c->zone) ShiftMaskPromise(base, shift, mask); +} + +Promise* combinedPromise(Context* c, Promise* low, Promise* high) { + return new (c->zone) CombinedPromise(low, high); +} + +Promise* resolvedPromise(Context* c, int64_t value) { + return new (c->zone) ResolvedPromise(value); +} + +class IpPromise: public Promise { + public: + IpPromise(Context* c, int logicalIp): + c(c), + logicalIp(logicalIp) + { } + + virtual int64_t value() { + if (resolved()) { + return reinterpret_cast + (c->machineCode + machineOffset(c, logicalIp)); + } + + abort(c); + } + + virtual bool resolved() { + return c->machineCode != 0 + and c->logicalCode[logicalIp]->machineOffset->resolved(); + } + + Context* c; + int logicalIp; +}; + +Promise* ipPromise(Context* c, int logicalIp) { + return new (c->zone) IpPromise(c, logicalIp); +} + + +class PoolPromise: public Promise { + public: + PoolPromise(Context* c, int key): c(c), key(key) { } + + virtual int64_t value() { + if (resolved()) { + return reinterpret_cast + (c->machineCode + vm::pad(c->machineCodeSize, vm::TargetBytesPerWord) + + (key * vm::TargetBytesPerWord)); + } + + abort(c); + } + + virtual bool resolved() { + return c->machineCode != 0; + } + + Context* c; + int key; +}; + +Promise* poolPromise(Context* c, int key) { + return new(c->zone) PoolPromise(c, key); +} + + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/promise.h b/src/codegen/compiler/promise.h new file mode 100644 index 0000000000..516782e0cd --- /dev/null +++ b/src/codegen/compiler/promise.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_PROMISE_H +#define AVIAN_CODEGEN_COMPILER_PROMISE_H + +namespace avian { +namespace codegen { +namespace compiler { + + +class CodePromise: public Promise { + public: + CodePromise(Context* c, CodePromise* next); + + CodePromise(Context* c, Promise* offset); + + virtual int64_t value(); + + virtual bool resolved(); + + Context* c; + Promise* offset; + CodePromise* next; +}; + +CodePromise* codePromise(Context* c, Promise* offset); + +Promise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask); + +Promise* combinedPromise(Context* c, Promise* low, Promise* high); + +Promise* resolvedPromise(Context* c, int64_t value); + +Promise* ipPromise(Context* c, int logicalIp); + +Promise* poolPromise(Context* c, int key); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_PROMISE_H diff --git a/src/codegen/compiler/read.cpp b/src/codegen/compiler/read.cpp new file mode 100644 index 0000000000..aa6b270cb0 --- /dev/null +++ b/src/codegen/compiler/read.cpp @@ -0,0 +1,191 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/read.h" + +namespace avian { +namespace codegen { +namespace compiler { + + +SingleRead::SingleRead(const SiteMask& mask, Value* successor): + next_(0), mask(mask), high_(0), successor_(successor) +{ } + +bool SingleRead::intersect(SiteMask* mask, unsigned) { + *mask = mask->intersectionWith(this->mask); + + return true; +} + +Value* SingleRead::high(Context*) { + return high_; +} + +Value* SingleRead::successor() { + return successor_; +} + +bool SingleRead::valid() { + return true; +} + +void SingleRead::append(Context* c UNUSED, Read* r) { + assert(c, next_ == 0); + next_ = r; +} + +Read* SingleRead::next(Context*) { + return next_; +} + +MultiRead::MultiRead(): + reads(0), lastRead(0), firstTarget(0), lastTarget(0), visited(false) +{ } + +bool MultiRead::intersect(SiteMask* mask, unsigned depth) { + if (depth > 0) { + // short-circuit recursion to avoid poor performance in + // deeply-nested branches + return reads != 0; + } + + bool result = false; + if (not visited) { + visited = true; + for (Cell** cell = &reads; *cell;) { + Read* r = (*cell)->value; + bool valid = r->intersect(mask, depth + 1); + if (valid) { + result = true; + cell = &((*cell)->next); + } else { + *cell = (*cell)->next; + } + } + visited = false; + } + return result; +} + +Value* MultiRead::successor() { + return 0; +} + +bool MultiRead::valid() { + bool result = false; + if (not visited) { + visited = true; + for (Cell** cell = &reads; *cell;) { + Read* r = (*cell)->value; + if (r->valid()) { + result = true; + cell = &((*cell)->next); + } else { + *cell = (*cell)->next; + } + } + visited = false; + } + return result; +} + +void MultiRead::append(Context* c, Read* r) { + Cell* cell = cons(c, r, 0); + if (lastRead == 0) { + reads = cell; + } else { + lastRead->next = cell; + } + lastRead = cell; + +// fprintf(stderr, "append %p to %p for %p\n", r, lastTarget, this); + + lastTarget->value = r; +} + +Read* MultiRead::next(Context* c) { + abort(c); +} + +void MultiRead::allocateTarget(Context* c) { + Cell* cell = cons(c, 0, 0); + +// fprintf(stderr, "allocate target for %p: %p\n", this, cell); + + if (lastTarget) { + lastTarget->next = cell; + } else { + firstTarget = cell; + } + lastTarget = cell; +} + +Read* MultiRead::nextTarget() { + // fprintf(stderr, "next target for %p: %p\n", this, firstTarget); + + Read* r = firstTarget->value; + firstTarget = firstTarget->next; + return r; +} + + +StubRead::StubRead(): + next_(0), read(0), visited(false), valid_(true) +{ } + +bool StubRead::intersect(SiteMask* mask, unsigned depth) { + if (not visited) { + visited = true; + if (read) { + bool valid = read->intersect(mask, depth); + if (not valid) { + read = 0; + } + } + visited = false; + } + return valid_; +} + +Value* StubRead::successor() { + return 0; +} + +bool StubRead::valid() { + return valid_; +} + +void StubRead::append(Context* c UNUSED, Read* r) { + assert(c, next_ == 0); + next_ = r; +} + +Read* StubRead::next(Context*) { + return next_; +} + + + +SingleRead* read(Context* c, const SiteMask& mask, Value* successor) { + assert(c, (mask.typeMask != 1 << lir::MemoryOperand) or mask.frameIndex >= 0); + + return new(c->zone) SingleRead(mask, successor); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/read.h b/src/codegen/compiler/read.h new file mode 100644 index 0000000000..e221dfb4bb --- /dev/null +++ b/src/codegen/compiler/read.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_READ_H +#define AVIAN_CODEGEN_COMPILER_READ_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class SiteMask; +class Value; +class Event; + +class Read { + public: + Read(): + value(0), event(0), eventNext(0) + { } + + virtual bool intersect(SiteMask* mask, unsigned depth = 0) = 0; + + virtual Value* high(Context* c) { abort(c); } + + virtual Value* successor() = 0; + + virtual bool valid() = 0; + + virtual void append(Context* c, Read* r) = 0; + + virtual Read* next(Context* c) = 0; + + Value* value; + Event* event; + Read* eventNext; +}; + +inline bool valid(Read* r) { + return r and r->valid(); +} + +class SingleRead: public Read { + public: + SingleRead(const SiteMask& mask, Value* successor); + + virtual bool intersect(SiteMask* mask, unsigned); + + virtual Value* high(Context*); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c UNUSED, Read* r); + + virtual Read* next(Context*); + + Read* next_; + SiteMask mask; + Value* high_; + Value* successor_; +}; + + +class MultiRead: public Read { + public: + MultiRead(); + + virtual bool intersect(SiteMask* mask, unsigned depth); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c, Read* r); + + virtual Read* next(Context* c); + + void allocateTarget(Context* c); + + Read* nextTarget(); + + Cell* reads; + Cell* lastRead; + Cell* firstTarget; + Cell* lastTarget; + bool visited; +}; + +class StubRead: public Read { + public: + StubRead(); + + virtual bool intersect(SiteMask* mask, unsigned depth); + + virtual Value* successor(); + + virtual bool valid(); + + virtual void append(Context* c UNUSED, Read* r); + + virtual Read* next(Context*); + + Read* next_; + Read* read; + bool visited; + bool valid_; +}; + +SingleRead* read(Context* c, const SiteMask& mask, Value* successor = 0); + + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_READ_H diff --git a/src/codegen/compiler/regalloc.cpp b/src/codegen/compiler/regalloc.cpp new file mode 100644 index 0000000000..47501fdb87 --- /dev/null +++ b/src/codegen/compiler/regalloc.cpp @@ -0,0 +1,300 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/context.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/read.h" + +namespace avian { +namespace codegen { +namespace compiler { + +RegisterAllocator::RegisterAllocator(Aborter* a, const RegisterFile* registerFile): + a(a), + registerFile(registerFile) +{ } + + +unsigned totalFrameSize(Context* c); +Read* live(Context* c UNUSED, Value* v); + +unsigned +resourceCost(Context* c, Value* v, Resource* r, SiteMask mask, + CostCalculator* costCalculator) +{ + if (r->reserved or r->freezeCount or r->referenceCount) { + return Target::Impossible; + } else { + unsigned baseCost = + costCalculator ? costCalculator->cost(c, mask) : 0; + + if (r->value) { + assert(c, r->value->findSite(r->site)); + + if (v and r->value->isBuddyOf(v)) { + return baseCost; + } else if (r->value->uniqueSite(c, r->site)) { + return baseCost + Target::StealUniquePenalty; + } else { + return baseCost = Target::StealPenalty; + } + } else { + return baseCost; + } + } +} + +bool +pickRegisterTarget(Context* c, int i, Value* v, uint32_t mask, int* target, + unsigned* cost, CostCalculator* costCalculator) +{ + if ((1 << i) & mask) { + RegisterResource* r = c->registerResources + i; + unsigned myCost = resourceCost + (c, v, r, SiteMask(1 << lir::RegisterOperand, 1 << i, NoFrameIndex), costCalculator) + + Target::MinimumRegisterCost; + + if ((static_cast(1) << i) == mask) { + *cost = myCost; + return true; + } else if (myCost < *cost) { + *cost = myCost; + *target = i; + } + } + return false; +} + +int +pickRegisterTarget(Context* c, Value* v, uint32_t mask, unsigned* cost, + CostCalculator* costCalculator) +{ + int target = lir::NoRegister; + *cost = Target::Impossible; + + if (mask & c->regFile->generalRegisters.mask) { + for (int i = c->regFile->generalRegisters.limit - 1; + i >= c->regFile->generalRegisters.start; --i) + { + if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { + return i; + } + } + } + + if (mask & c->regFile->floatRegisters.mask) { + for (int i = c->regFile->floatRegisters.start; + i < static_cast(c->regFile->floatRegisters.limit); ++i) + { + if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { + return i; + } + } + } + + return target; +} + +Target +pickRegisterTarget(Context* c, Value* v, uint32_t mask, + CostCalculator* costCalculator) +{ + unsigned cost; + int number = pickRegisterTarget(c, v, mask, &cost, costCalculator); + return Target(number, lir::RegisterOperand, cost); +} + +unsigned +frameCost(Context* c, Value* v, int frameIndex, CostCalculator* costCalculator) +{ + return resourceCost + (c, v, c->frameResources + frameIndex, SiteMask(1 << lir::MemoryOperand, 0, frameIndex), + costCalculator) + + Target::MinimumFrameCost; +} + +Target +pickFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) +{ + Target best; + + Value* p = v; + do { + if (p->home >= 0) { + Target mine + (p->home, lir::MemoryOperand, frameCost(c, v, p->home, costCalculator)); + + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + p = p->buddy; + } while (p != v); + + return best; +} + +Target +pickAnyFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) +{ + Target best; + + unsigned count = totalFrameSize(c); + for (unsigned i = 0; i < count; ++i) { + Target mine(i, lir::MemoryOperand, frameCost(c, v, i, costCalculator)); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + return best; +} + +Target +pickTarget(Context* c, Value* value, const SiteMask& mask, + unsigned registerPenalty, Target best, + CostCalculator* costCalculator) +{ + if (mask.typeMask & (1 << lir::RegisterOperand)) { + Target mine = pickRegisterTarget + (c, value, mask.registerMask, costCalculator); + + mine.cost += registerPenalty; + if (mine.cost == Target::MinimumRegisterCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + if (mask.typeMask & (1 << lir::MemoryOperand)) { + if (mask.frameIndex >= 0) { + Target mine(mask.frameIndex, lir::MemoryOperand, + frameCost(c, value, mask.frameIndex, costCalculator)); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } else if (mask.frameIndex == AnyFrameIndex) { + Target mine = pickFrameTarget(c, value, costCalculator); + 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, CostCalculator* costCalculator) +{ + unsigned registerPenalty + = (c->availableGeneralRegisterCount > registerReserveCount + ? 0 : Target::LowRegisterPenalty); + + Value* value = read->value; + + uint32_t registerMask + = (value->type == lir::ValueFloat ? ~0 : c->regFile->generalRegisters.mask); + + SiteMask mask(~0, registerMask, AnyFrameIndex); + read->intersect(&mask); + + if (value->type == lir::ValueFloat) { + uint32_t floatMask = mask.registerMask & c->regFile->floatRegisters.mask; + if (floatMask) { + mask.registerMask = floatMask; + } + } + + Target best; + + Value* successor = read->successor(); + if (successor) { + Read* r = live(c, successor); + if (r) { + SiteMask intersection = mask; + if (r->intersect(&intersection)) { + best = pickTarget + (c, value, intersection, registerPenalty, best, costCalculator); + + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + } + } + } + + best = pickTarget(c, value, mask, registerPenalty, best, costCalculator); + if (best.cost <= Target::MinimumFrameCost) { + return best; + } + + if (intersectRead) { + if (best.cost == Target::Impossible) { + fprintf(stderr, "mask type %d reg %d frame %d\n", + mask.typeMask, mask.registerMask, mask.frameIndex); + abort(c); + } + return best; + } + + { Target mine = pickRegisterTarget(c, value, registerMask, costCalculator); + + mine.cost += registerPenalty; + + if (mine.cost == Target::MinimumRegisterCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + { Target mine = pickFrameTarget(c, value, costCalculator); + if (mine.cost == Target::MinimumFrameCost) { + return mine; + } else if (mine.cost < best.cost) { + best = mine; + } + } + + if (best.cost >= Target::StealUniquePenalty + and c->availableGeneralRegisterCount == 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: + best = pickAnyFrameTarget(c, value, costCalculator); + assert(c, best.cost <= 3); + } + + if (best.cost == Target::Impossible) { + abort(c); + } + + return best; +} + +} // namespace regalloc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/regalloc.h b/src/codegen/compiler/regalloc.h new file mode 100644 index 0000000000..66ab860a07 --- /dev/null +++ b/src/codegen/compiler/regalloc.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_REGALLOC_H +#define AVIAN_CODEGEN_COMPILER_REGALLOC_H + +#include "avian/common.h" + +#include +#include + +namespace avian { + +namespace util { +class Aborter; +} // namespace util + +namespace codegen { +namespace compiler { + +using namespace avian::util; + +class Context; +class Value; +class SiteMask; +class Resource; +class Read; + + +class RegisterAllocator { +public: + Aborter* a; + const RegisterFile* registerFile; + + RegisterAllocator(Aborter* a, const RegisterFile* registerFile); + +}; + +class Target { + public: + static const unsigned MinimumRegisterCost = 0; + static const unsigned MinimumFrameCost = 1; + static const unsigned StealPenalty = 2; + static const unsigned StealUniquePenalty = 4; + static const unsigned IndirectMovePenalty = 4; + static const unsigned LowRegisterPenalty = 10; + static const unsigned Impossible = 20; + + Target(): cost(Impossible) { } + + Target(int index, lir::OperandType type, unsigned cost): + index(index), type(type), cost(cost) + { } + + int16_t index; + lir::OperandType type; + uint8_t cost; +}; + +class CostCalculator { + public: + virtual unsigned cost(Context* c, SiteMask mask) = 0; +}; + +unsigned +resourceCost(Context* c, Value* v, Resource* r, SiteMask mask, + CostCalculator* costCalculator); + + +bool +pickRegisterTarget(Context* c, int i, Value* v, uint32_t mask, int* target, + unsigned* cost, CostCalculator* costCalculator = 0); + +int +pickRegisterTarget(Context* c, Value* v, uint32_t mask, unsigned* cost, + CostCalculator* costCalculator = 0); + +Target +pickRegisterTarget(Context* c, Value* v, uint32_t mask, + CostCalculator* costCalculator = 0); + +unsigned +frameCost(Context* c, Value* v, int frameIndex, CostCalculator* costCalculator); + +Target +pickFrameTarget(Context* c, Value* v, CostCalculator* costCalculator); + +Target +pickAnyFrameTarget(Context* c, Value* v, CostCalculator* costCalculator); + +Target +pickTarget(Context* c, Value* value, const SiteMask& mask, + unsigned registerPenalty, Target best, + CostCalculator* costCalculator); + +Target +pickTarget(Context* c, Read* read, bool intersectRead, + unsigned registerReserveCount, CostCalculator* costCalculator); + +} // namespace regalloc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_REGALLOC_H \ No newline at end of file diff --git a/src/codegen/compiler/resource.cpp b/src/codegen/compiler/resource.cpp new file mode 100644 index 0000000000..af5e917762 --- /dev/null +++ b/src/codegen/compiler/resource.cpp @@ -0,0 +1,226 @@ +/* Copyright (c) 2008-2012, 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 "codegen/compiler/context.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/value.h" + +namespace avian { +namespace codegen { +namespace compiler { + +const bool DebugResources = false; + + +void steal(Context* c, Resource* r, Value* thief); + +void decrementAvailableGeneralRegisterCount(Context* c) { + assert(c, c->availableGeneralRegisterCount); + -- c->availableGeneralRegisterCount; + + if (DebugResources) { + fprintf(stderr, "%d registers available\n", + c->availableGeneralRegisterCount); + } +} + +void incrementAvailableGeneralRegisterCount(Context* c) { + ++ c->availableGeneralRegisterCount; + + if (DebugResources) { + fprintf(stderr, "%d registers available\n", + c->availableGeneralRegisterCount); + } +} + +void freezeResource(Context* c, Resource* r, Value* v) { + if (DebugResources) { + char buffer[256]; r->toString(c, buffer, 256); + fprintf(stderr, "%p freeze %s to %d\n", v, buffer, r->freezeCount + 1); + } + + ++ r->freezeCount; +} + +void thawResource(Context* c, Resource* r, Value* v) { + if (not r->reserved) { + if (DebugResources) { + char buffer[256]; r->toString(c, buffer, 256); + fprintf(stderr, "%p thaw %s to %d\n", v, buffer, r->freezeCount - 1); + } + + assert(c, r->freezeCount); + + -- r->freezeCount; + } +} + + +Resource::Resource(bool reserved): + value(0), site(0), previousAcquired(0), nextAcquired(0), freezeCount(0), + referenceCount(0), reserved(reserved) +{ } + +RegisterResource::RegisterResource(bool reserved): + Resource(reserved) +{ } + +void RegisterResource::freeze(Context* c, Value* v) { + if (not reserved) { + freezeResource(c, this, v); + + if (freezeCount == 1 + and ((1 << index(c)) & c->regFile->generalRegisters.mask)) + { + decrementAvailableGeneralRegisterCount(c); + } + } +} + +void RegisterResource::thaw(Context* c, Value* v) { + if (not reserved) { + thawResource(c, this, v); + + if (freezeCount == 0 + and ((1 << index(c)) & c->regFile->generalRegisters.mask)) + { + incrementAvailableGeneralRegisterCount(c); + } + } +} + +unsigned RegisterResource::toString(Context* c, char* buffer, unsigned bufferSize) { + return vm::snprintf(buffer, bufferSize, "register %d", index(c)); +} + +unsigned RegisterResource::index(Context* c) { + return this - c->registerResources; +} + +void RegisterResource::increment(Context* c) { + if (not this->reserved) { + if (DebugResources) { + char buffer[256]; this->toString(c, buffer, 256); + fprintf(stderr, "increment %s to %d\n", buffer, this->referenceCount + 1); + } + + ++ this->referenceCount; + + if (this->referenceCount == 1 + and ((1 << this->index(c)) & c->regFile->generalRegisters.mask)) + { + decrementAvailableGeneralRegisterCount(c); + } + } +} + +void RegisterResource::decrement(Context* c) { + if (not this->reserved) { + if (DebugResources) { + char buffer[256]; this->toString(c, buffer, 256); + fprintf(stderr, "decrement %s to %d\n", buffer, this->referenceCount - 1); + } + + assert(c, this->referenceCount > 0); + + -- this->referenceCount; + + if (this->referenceCount == 0 + and ((1 << this->index(c)) & c->regFile->generalRegisters.mask)) + { + incrementAvailableGeneralRegisterCount(c); + } + } +} + + + +void FrameResource::freeze(Context* c, Value* v) { + freezeResource(c, this, v); +} + +void FrameResource::thaw(Context* c, Value* v) { + thawResource(c, this, v); +} + +unsigned FrameResource::toString(Context* c, char* buffer, unsigned bufferSize) { + return vm::snprintf(buffer, bufferSize, "frame %d", index(c)); +} + +unsigned FrameResource::index(Context* c) { + return this - c->frameResources; +} + + +void acquire(Context* c, Resource* resource, Value* value, Site* site) { + assert(c, value); + assert(c, site); + + if (not resource->reserved) { + if (DebugResources) { + char buffer[256]; resource->toString(c, buffer, 256); + fprintf(stderr, "%p acquire %s\n", value, buffer); + } + + if (resource->value) { + assert(c, resource->value->findSite(resource->site)); + assert(c, not value->findSite(resource->site)); + + steal(c, resource, value); + } + + if (c->acquiredResources) { + c->acquiredResources->previousAcquired = resource; + resource->nextAcquired = c->acquiredResources; + } + c->acquiredResources = resource; + + resource->value = value; + resource->site = site; + } +} + +void release(Context* c, Resource* resource, Value* value UNUSED, Site* site UNUSED) { + if (not resource->reserved) { + if (DebugResources) { + char buffer[256]; resource->toString(c, buffer, 256); + fprintf(stderr, "%p release %s\n", resource->value, buffer); + } + + assert(c, resource->value); + assert(c, resource->site); + + assert(c, resource->value->isBuddyOf(value)); + assert(c, site == resource->site); + + Resource* next = resource->nextAcquired; + if (next) { + next->previousAcquired = resource->previousAcquired; + resource->nextAcquired = 0; + } + + Resource* previous = resource->previousAcquired; + if (previous) { + previous->nextAcquired = next; + resource->previousAcquired = 0; + } else { + assert(c, c->acquiredResources == resource); + c->acquiredResources = next; + } + + resource->value = 0; + resource->site = 0; + } +} + + +} // namespace compiler +} // namespace codegen +} // namespace avian \ No newline at end of file diff --git a/src/codegen/compiler/resource.h b/src/codegen/compiler/resource.h new file mode 100644 index 0000000000..1cc35e3445 --- /dev/null +++ b/src/codegen/compiler/resource.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_RESOURCE_H +#define AVIAN_CODEGEN_COMPILER_RESOURCE_H + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; +class Value; +class Site; + +class Resource { + public: + Resource(bool reserved = false); + + virtual void freeze(Context*, Value*) = 0; + + virtual void thaw(Context*, Value*) = 0; + + virtual unsigned toString(Context*, char*, unsigned) = 0; + + Value* value; + Site* site; + Resource* previousAcquired; + Resource* nextAcquired; + uint8_t freezeCount; + uint8_t referenceCount; + bool reserved; +}; + +class RegisterResource: public Resource { + public: + RegisterResource(bool reserved); + + virtual void freeze(Context*, Value*); + + virtual void thaw(Context*, Value*); + + virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize); + + virtual unsigned index(Context*); + + void increment(Context*); + + void decrement(Context*); +}; + +class FrameResource: public Resource { + public: + virtual void freeze(Context*, Value*); + + virtual void thaw(Context*, Value*); + + virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize); + + virtual unsigned index(Context*); +}; + +void acquire(Context* c, Resource* resource, Value* value, Site* site); + +void release(Context* c, Resource* resource, Value* value, Site* site); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_RESOURCE_H diff --git a/src/codegen/compiler/site.cpp b/src/codegen/compiler/site.cpp new file mode 100644 index 0000000000..30742a56ae --- /dev/null +++ b/src/codegen/compiler/site.cpp @@ -0,0 +1,628 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/context.h" +#include "codegen/compiler/value.h" +#include "codegen/compiler/site.h" +#include "codegen/compiler/resource.h" +#include "codegen/compiler/frame.h" +#include "codegen/compiler/promise.h" + +namespace avian { +namespace codegen { +namespace compiler { + + + +int intersectFrameIndexes(int a, int b) { + if (a == NoFrameIndex or b == NoFrameIndex) return NoFrameIndex; + if (a == AnyFrameIndex) return b; + if (b == AnyFrameIndex) return a; + if (a == b) return a; + return NoFrameIndex; +} + + +SiteMask SiteMask::intersectionWith(const SiteMask& b) { + return SiteMask(typeMask & b.typeMask, registerMask & b.registerMask, + intersectFrameIndexes(frameIndex, b.frameIndex)); +} + +SiteIterator::SiteIterator(Context* c, Value* v, bool includeBuddies, + bool includeNextWord): + c(c), + originalValue(v), + currentValue(v), + includeBuddies(includeBuddies), + includeNextWord(includeNextWord), + pass(0), + next_(findNext(&(v->sites))), + previous(0) +{ } + +Site** SiteIterator::findNext(Site** p) { + while (true) { + if (*p) { + if (pass == 0 or (*p)->registerSize(c) > vm::TargetBytesPerWord) { + return p; + } else { + p = &((*p)->next); + } + } else { + if (includeBuddies) { + Value* v = currentValue->buddy; + if (v != originalValue) { + currentValue = v; + p = &(v->sites); + continue; + } + } + + if (includeNextWord and pass == 0) { + Value* v = originalValue->nextWord; + if (v != originalValue) { + pass = 1; + originalValue = v; + currentValue = v; + p = &(v->sites); + continue; + } + } + + return 0; + } + } +} + +bool SiteIterator::hasMore() { + if (previous) { + next_ = findNext(&((*previous)->next)); + previous = 0; + } + return next_ != 0; +} + +Site* SiteIterator::next() { + previous = next_; + return *previous; +} + +void SiteIterator::remove(Context* c) { + (*previous)->release(c, originalValue); + *previous = (*previous)->next; + next_ = findNext(previous); + previous = 0; +} + + + +unsigned Site::registerSize(Context*) { + return vm::TargetBytesPerWord; +} + + + +Site* constantSite(Context* c, Promise* value) { + return new(c->zone) ConstantSite(value); +} + +Site* constantSite(Context* c, int64_t value) { + return constantSite(c, resolvedPromise(c, value)); +} + + + +class AddressSite: public Site { + public: + AddressSite(Promise* address): address(address) { } + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { + if (address->resolved()) { + return vm::snprintf + (buffer, bufferSize, "address %" LLD, address->value()); + } else { + return vm::snprintf(buffer, bufferSize, "address unresolved"); + } + } + + virtual unsigned copyCost(Context*, Site* s) { + return (s == this ? 0 : AddressCopyCost); + } + + virtual bool match(Context*, const SiteMask& mask) { + return mask.typeMask & (1 << lir::AddressOperand); + } + + virtual bool loneMatch(Context*, const SiteMask&) { + return false; + } + + virtual bool matchNextWord(Context* c, Site*, unsigned) { + abort(c); + } + + virtual lir::OperandType type(Context*) { + return lir::AddressOperand; + } + + virtual void asAssemblerOperand(Context* c UNUSED, Site* high UNUSED, + lir::Operand* result) + { + assert(c, high == this); + + new (result) lir::Address(address); + } + + virtual Site* copy(Context* c) { + return addressSite(c, address); + } + + virtual Site* copyLow(Context* c) { + abort(c); + } + + virtual Site* copyHigh(Context* c) { + abort(c); + } + + virtual Site* makeNextWord(Context* c, unsigned) { + abort(c); + } + + virtual SiteMask mask(Context*) { + return SiteMask(1 << lir::AddressOperand, 0, NoFrameIndex); + } + + virtual SiteMask nextWordMask(Context* c, unsigned) { + abort(c); + } + + Promise* address; +}; + +Site* addressSite(Context* c, Promise* address) { + return new(c->zone) AddressSite(address); +} + + +RegisterSite::RegisterSite(uint32_t mask, int number): + mask_(mask), number(number) +{ } + +unsigned RegisterSite::toString(Context*, char* buffer, unsigned bufferSize) { + if (number != lir::NoRegister) { + return vm::snprintf(buffer, bufferSize, "%p register %d", this, number); + } else { + return vm::snprintf(buffer, bufferSize, + "%p register unacquired (mask %d)", this, mask_); + } +} + +unsigned RegisterSite::copyCost(Context* c, Site* s) { + assert(c, number != lir::NoRegister); + + if (s and + (this == s or + (s->type(c) == lir::RegisterOperand + and (static_cast(s)->mask_ & (1 << number))))) + { + return 0; + } else { + return RegisterCopyCost; + } +} + +bool RegisterSite::match(Context* c UNUSED, const SiteMask& mask) { + assert(c, number != lir::NoRegister); + + if ((mask.typeMask & (1 << lir::RegisterOperand))) { + return ((static_cast(1) << number) & mask.registerMask); + } else { + return false; + } +} + +bool RegisterSite::loneMatch(Context* c UNUSED, const SiteMask& mask) { + assert(c, number != lir::NoRegister); + + if ((mask.typeMask & (1 << lir::RegisterOperand))) { + return ((static_cast(1) << number) == mask.registerMask); + } else { + return false; + } +} + +bool RegisterSite::matchNextWord(Context* c, Site* s, unsigned) { + assert(c, number != lir::NoRegister); + + if (s->type(c) != lir::RegisterOperand) { + return false; + } + + RegisterSite* rs = static_cast(s); + unsigned size = rs->registerSize(c); + if (size > vm::TargetBytesPerWord) { + assert(c, number != lir::NoRegister); + return number == rs->number; + } else { + uint32_t mask = c->regFile->generalRegisters.mask; + return ((1 << number) & mask) and ((1 << rs->number) & mask); + } +} + +void RegisterSite::acquire(Context* c, Value* v) { + Target target; + if (number != lir::NoRegister) { + target = Target(number, lir::RegisterOperand, 0); + } else { + target = pickRegisterTarget(c, v, mask_); + expect(c, target.cost < Target::Impossible); + } + + RegisterResource* resource = c->registerResources + target.index; + compiler::acquire(c, resource, v, this); + + number = target.index; +} + +void RegisterSite::release(Context* c, Value* v) { + assert(c, number != lir::NoRegister); + + compiler::release(c, c->registerResources + number, v, this); +} + +void RegisterSite::freeze(Context* c, Value* v) { + assert(c, number != lir::NoRegister); + + c->registerResources[number].freeze(c, v); +} + +void RegisterSite::thaw(Context* c, Value* v) { + assert(c, number != lir::NoRegister); + + c->registerResources[number].thaw(c, v); +} + +bool RegisterSite::frozen(Context* c UNUSED) { + assert(c, number != lir::NoRegister); + + return c->registerResources[number].freezeCount != 0; +} + +lir::OperandType RegisterSite::type(Context*) { + return lir::RegisterOperand; +} + +void RegisterSite::asAssemblerOperand(Context* c UNUSED, Site* high, + lir::Operand* result) +{ + assert(c, number != lir::NoRegister); + + int highNumber; + if (high != this) { + highNumber = static_cast(high)->number; + assert(c, highNumber != lir::NoRegister); + } else { + highNumber = lir::NoRegister; + } + + new (result) lir::Register(number, highNumber); +} + +Site* RegisterSite::copy(Context* c) { + uint32_t mask; + + if (number != lir::NoRegister) { + mask = 1 << number; + } else { + mask = mask_; + } + + return freeRegisterSite(c, mask); +} + +Site* RegisterSite::copyLow(Context* c) { + abort(c); +} + +Site* RegisterSite::copyHigh(Context* c) { + abort(c); +} + +Site* RegisterSite::makeNextWord(Context* c, unsigned) { + assert(c, number != lir::NoRegister); + assert(c, ((1 << number) & c->regFile->generalRegisters.mask)); + + return freeRegisterSite(c, c->regFile->generalRegisters.mask); +} + +SiteMask RegisterSite::mask(Context* c UNUSED) { + return SiteMask(1 << lir::RegisterOperand, mask_, NoFrameIndex); +} + +SiteMask RegisterSite::nextWordMask(Context* c, unsigned) { + assert(c, number != lir::NoRegister); + + if (registerSize(c) > vm::TargetBytesPerWord) { + return SiteMask + (1 << lir::RegisterOperand, number, NoFrameIndex); + } else { + return SiteMask + (1 << lir::RegisterOperand, c->regFile->generalRegisters.mask, NoFrameIndex); + } +} + +unsigned RegisterSite::registerSize(Context* c) { + assert(c, number != lir::NoRegister); + + if ((1 << number) & c->regFile->floatRegisters.mask) { + return c->arch->floatRegisterSize(); + } else { + return vm::TargetBytesPerWord; + } +} + +unsigned RegisterSite::registerMask(Context* c UNUSED) { + assert(c, number != lir::NoRegister); + + return 1 << number; +} + + + +Site* registerSite(Context* c, int number) { + assert(c, number >= 0); + assert(c, (1 << number) & (c->regFile->generalRegisters.mask + | c->regFile->floatRegisters.mask)); + + return new(c->zone) RegisterSite(1 << number, number); +} + +Site* freeRegisterSite(Context* c, uint32_t mask) { + return new(c->zone) RegisterSite(mask, lir::NoRegister); +} + +MemorySite::MemorySite(int base, int offset, int index, unsigned scale): + acquired(false), base(base), offset(offset), index(index), scale(scale) +{ } + +unsigned MemorySite::toString(Context*, char* buffer, unsigned bufferSize) { + if (acquired) { + return vm::snprintf(buffer, bufferSize, "memory %d 0x%x %d %d", + base, offset, index, scale); + } else { + return vm::snprintf(buffer, bufferSize, "memory unacquired"); + } +} + +unsigned MemorySite::copyCost(Context* c, Site* s) { + assert(c, acquired); + + if (s and + (this == s or + (s->type(c) == lir::MemoryOperand + and static_cast(s)->base == base + and static_cast(s)->offset == offset + and static_cast(s)->index == index + and static_cast(s)->scale == scale))) + { + return 0; + } else { + return MemoryCopyCost; + } +} + +bool MemorySite::conflicts(const SiteMask& mask) { + return (mask.typeMask & (1 << lir::RegisterOperand)) != 0 + and (((1 << base) & mask.registerMask) == 0 + or (index != lir::NoRegister + and ((1 << index) & mask.registerMask) == 0)); +} + +bool MemorySite::match(Context* c, const SiteMask& mask) { + assert(c, acquired); + + if (mask.typeMask & (1 << lir::MemoryOperand)) { + if (mask.frameIndex >= 0) { + if (base == c->arch->stack()) { + assert(c, index == lir::NoRegister); + return static_cast(frameIndexToOffset(c, mask.frameIndex)) + == offset; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } +} + +bool MemorySite::loneMatch(Context* c, const SiteMask& mask) { + assert(c, acquired); + + if (mask.typeMask & (1 << lir::MemoryOperand)) { + if (base == c->arch->stack()) { + assert(c, index == lir::NoRegister); + + if (mask.frameIndex == AnyFrameIndex) { + return false; + } else { + return true; + } + } + } + return false; +} + +bool MemorySite::matchNextWord(Context* c, Site* s, unsigned index) { + if (s->type(c) == lir::MemoryOperand) { + MemorySite* ms = static_cast(s); + return ms->base == this->base + and ((index == 1 and ms->offset == static_cast + (this->offset + vm::TargetBytesPerWord)) + or (index == 0 and this->offset == static_cast + (ms->offset + vm::TargetBytesPerWord))) + and ms->index == this->index + and ms->scale == this->scale; + } else { + return false; + } +} + +void MemorySite::acquire(Context* c, Value* v) { + c->registerResources[base].increment(c); + if (index != lir::NoRegister) { + c->registerResources[index].increment(c); + } + + if (base == c->arch->stack()) { + assert(c, index == lir::NoRegister); + assert + (c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); + + compiler::acquire + (c, c->frameResources + offsetToFrameIndex(c, offset), v, this); + } + + acquired = true; +} + +void MemorySite::release(Context* c, Value* v) { + if (base == c->arch->stack()) { + assert(c, index == lir::NoRegister); + assert + (c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); + + compiler::release + (c, c->frameResources + offsetToFrameIndex(c, offset), v, this); + } + + c->registerResources[base].decrement(c); + if (index != lir::NoRegister) { + c->registerResources[index].decrement(c); + } + + acquired = false; +} + +void MemorySite::freeze(Context* c, Value* v) { + if (base == c->arch->stack()) { + c->frameResources[offsetToFrameIndex(c, offset)].freeze(c, v); + } else { + c->registerResources[base].increment(c); + if (index != lir::NoRegister) { + c->registerResources[index].increment(c); + } + } +} + +void MemorySite::thaw(Context* c, Value* v) { + if (base == c->arch->stack()) { + c->frameResources[offsetToFrameIndex(c, offset)].thaw(c, v); + } else { + c->registerResources[base].decrement(c); + if (index != lir::NoRegister) { + c->registerResources[index].decrement(c); + } + } +} + +bool MemorySite::frozen(Context* c) { + return base == c->arch->stack() + and c->frameResources[offsetToFrameIndex(c, offset)].freezeCount != 0; +} + +lir::OperandType MemorySite::type(Context*) { + return lir::MemoryOperand; +} + +void MemorySite::asAssemblerOperand(Context* c UNUSED, Site* high UNUSED, + lir::Operand* result) +{ + // todo: endianness? + assert(c, high == this + or (static_cast(high)->base == base + and static_cast(high)->offset + == static_cast(offset + vm::TargetBytesPerWord) + and static_cast(high)->index == index + and static_cast(high)->scale == scale)); + + assert(c, acquired); + + new (result) lir::Memory(base, offset, index, scale); +} + +Site* MemorySite::copy(Context* c) { + return memorySite(c, base, offset, index, scale); +} + +Site* MemorySite::copyHalf(Context* c, bool add) { + if (add) { + return memorySite(c, base, offset + vm::TargetBytesPerWord, index, scale); + } else { + return copy(c); + } +} + +Site* MemorySite::copyLow(Context* c) { + return copyHalf(c, c->arch->bigEndian()); +} + +Site* MemorySite::copyHigh(Context* c) { + return copyHalf(c, not c->arch->bigEndian()); +} + +Site* MemorySite::makeNextWord(Context* c, unsigned index) { + return memorySite + (c, base, offset + ((index == 1) xor c->arch->bigEndian() + ? vm::TargetBytesPerWord : -vm::TargetBytesPerWord), + this->index, scale); +} + +SiteMask MemorySite::mask(Context* c) { + return SiteMask(1 << lir::MemoryOperand, 0, (base == c->arch->stack()) + ? static_cast(offsetToFrameIndex(c, offset)) + : NoFrameIndex); +} + +SiteMask MemorySite::nextWordMask(Context* c, unsigned index) { + int frameIndex; + if (base == c->arch->stack()) { + assert(c, this->index == lir::NoRegister); + frameIndex = static_cast(offsetToFrameIndex(c, offset)) + + ((index == 1) xor c->arch->bigEndian() ? 1 : -1); + } else { + frameIndex = NoFrameIndex; + } + return SiteMask(1 << lir::MemoryOperand, 0, frameIndex); +} + +bool MemorySite::isVolatile(Context* c) { + return base != c->arch->stack(); +} + + +MemorySite* memorySite(Context* c, int base, int offset, int index, unsigned scale) { + return new(c->zone) MemorySite(base, offset, index, scale); +} + +MemorySite* frameSite(Context* c, int frameIndex) { + assert(c, frameIndex >= 0); + return memorySite + (c, c->arch->stack(), frameIndexToOffset(c, frameIndex), lir::NoRegister, 0); +} + +} // namespace compiler +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/site.h b/src/codegen/compiler/site.h new file mode 100644 index 0000000000..aedcc7ab09 --- /dev/null +++ b/src/codegen/compiler/site.h @@ -0,0 +1,321 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_SITE_H +#define AVIAN_CODEGEN_COMPILER_SITE_H + +#include + +#include "codegen/compiler/value.h" +#include "codegen/compiler/context.h" + +namespace avian { +namespace codegen { +namespace compiler { + +class Context; + +const unsigned RegisterCopyCost = 1; +const unsigned AddressCopyCost = 2; +const unsigned ConstantCopyCost = 3; +const unsigned MemoryCopyCost = 4; +const unsigned CopyPenalty = 10; + +class SiteMask { + public: + SiteMask(): typeMask(~0), registerMask(~0), frameIndex(AnyFrameIndex) { } + + SiteMask(uint8_t typeMask, uint32_t registerMask, int frameIndex): + typeMask(typeMask), registerMask(registerMask), frameIndex(frameIndex) + { } + + SiteMask intersectionWith(const SiteMask& b); + + static SiteMask fixedRegisterMask(int number) { + return SiteMask(1 << lir::RegisterOperand, 1 << number, NoFrameIndex); + } + + static SiteMask lowPart(const OperandMask& mask) { + return SiteMask(mask.typeMask, mask.registerMask, AnyFrameIndex); + } + + static SiteMask highPart(const OperandMask& mask) { + return SiteMask(mask.typeMask, mask.registerMask >> 32, AnyFrameIndex); + } + + uint8_t typeMask; + uint32_t registerMask; + int frameIndex; +}; + +class Site { + public: + Site(): next(0) { } + + virtual Site* readTarget(Context*, Read*) { return this; } + + virtual unsigned toString(Context*, char*, unsigned) = 0; + + virtual unsigned copyCost(Context*, Site*) = 0; + + virtual bool match(Context*, const SiteMask&) = 0; + + virtual bool loneMatch(Context*, const SiteMask&) = 0; + + virtual bool matchNextWord(Context*, Site*, unsigned) = 0; + + virtual void acquire(Context*, Value*) { } + + virtual void release(Context*, Value*) { } + + virtual void freeze(Context*, Value*) { } + + virtual void thaw(Context*, Value*) { } + + virtual bool frozen(Context*) { return false; } + + virtual lir::OperandType type(Context*) = 0; + + virtual void asAssemblerOperand(Context*, Site*, lir::Operand*) = 0; + + virtual Site* copy(Context*) = 0; + + virtual Site* copyLow(Context*) = 0; + + virtual Site* copyHigh(Context*) = 0; + + virtual Site* makeNextWord(Context*, unsigned) = 0; + + virtual SiteMask mask(Context*) = 0; + + virtual SiteMask nextWordMask(Context*, unsigned) = 0; + + virtual unsigned registerSize(Context*); + + virtual unsigned registerMask(Context*) { return 0; } + + virtual bool isVolatile(Context*) { return false; } + + Site* next; +}; + +class SiteIterator { + public: + SiteIterator(Context* c, Value* v, bool includeBuddies = true, + bool includeNextWord = true); + + Site** findNext(Site** p); + bool hasMore(); + Site* next(); + void remove(Context* c); + + Context* c; + Value* originalValue; + Value* currentValue; + bool includeBuddies; + bool includeNextWord; + uint8_t pass; + Site** next_; + Site** previous; +}; + +Site* constantSite(Context* c, Promise* value); +Site* constantSite(Context* c, int64_t value); + +Promise* combinedPromise(Context* c, Promise* low, Promise* high); +Promise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask); + +class ConstantSite: public Site { + public: + ConstantSite(Promise* value): value(value) { } + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { + if (value->resolved()) { + return vm::snprintf + (buffer, bufferSize, "constant %" LLD, value->value()); + } else { + return vm::snprintf(buffer, bufferSize, "constant unresolved"); + } + } + + virtual unsigned copyCost(Context*, Site* s) { + return (s == this ? 0 : ConstantCopyCost); + } + + virtual bool match(Context*, const SiteMask& mask) { + return mask.typeMask & (1 << lir::ConstantOperand); + } + + virtual bool loneMatch(Context*, const SiteMask&) { + return true; + } + + virtual bool matchNextWord(Context* c, Site* s, unsigned) { + return s->type(c) == lir::ConstantOperand; + } + + virtual lir::OperandType type(Context*) { + return lir::ConstantOperand; + } + + virtual void asAssemblerOperand(Context* c, Site* high, + lir::Operand* result) + { + Promise* v = value; + if (high != this) { + v = combinedPromise(c, value, static_cast(high)->value); + } + new (result) lir::Constant(v); + } + + virtual Site* copy(Context* c) { + return constantSite(c, value); + } + + virtual Site* copyLow(Context* c) { + return constantSite(c, shiftMaskPromise(c, value, 0, 0xFFFFFFFF)); + } + + virtual Site* copyHigh(Context* c) { + return constantSite(c, shiftMaskPromise(c, value, 32, 0xFFFFFFFF)); + } + + virtual Site* makeNextWord(Context* c, unsigned) { + abort(c); + } + + virtual SiteMask mask(Context*) { + return SiteMask(1 << lir::ConstantOperand, 0, NoFrameIndex); + } + + virtual SiteMask nextWordMask(Context*, unsigned) { + return SiteMask(1 << lir::ConstantOperand, 0, NoFrameIndex); + } + + Promise* value; +}; + +Site* addressSite(Context* c, Promise* address); + +class RegisterSite: public Site { + public: + RegisterSite(uint32_t mask, int number); + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize); + + virtual unsigned copyCost(Context* c, Site* s); + + virtual bool match(Context* c UNUSED, const SiteMask& mask); + + virtual bool loneMatch(Context* c UNUSED, const SiteMask& mask); + + virtual bool matchNextWord(Context* c, Site* s, unsigned); + + virtual void acquire(Context* c, Value* v); + + virtual void release(Context* c, Value* v); + + virtual void freeze(Context* c, Value* v); + + virtual void thaw(Context* c, Value* v); + + virtual bool frozen(Context* c UNUSED); + + virtual lir::OperandType type(Context*); + + virtual void asAssemblerOperand(Context* c UNUSED, Site* high, + lir::Operand* result); + + virtual Site* copy(Context* c); + + virtual Site* copyLow(Context* c); + + virtual Site* copyHigh(Context* c); + + virtual Site* makeNextWord(Context* c, unsigned); + + virtual SiteMask mask(Context* c UNUSED); + + virtual SiteMask nextWordMask(Context* c, unsigned); + + virtual unsigned registerSize(Context* c); + + virtual unsigned registerMask(Context* c UNUSED); + + uint32_t mask_; + int number; +}; + +Site* registerSite(Context* c, int number); +Site* freeRegisterSite(Context* c, uint32_t mask); + + +class MemorySite: public Site { + public: + MemorySite(int base, int offset, int index, unsigned scale); + + virtual unsigned toString(Context*, char* buffer, unsigned bufferSize); + + virtual unsigned copyCost(Context* c, Site* s); + + bool conflicts(const SiteMask& mask); + + virtual bool match(Context* c, const SiteMask& mask); + + virtual bool loneMatch(Context* c, const SiteMask& mask); + + virtual bool matchNextWord(Context* c, Site* s, unsigned index); + + virtual void acquire(Context* c, Value* v); + + virtual void release(Context* c, Value* v); + + virtual void freeze(Context* c, Value* v); + + virtual void thaw(Context* c, Value* v); + + virtual bool frozen(Context* c); + + virtual lir::OperandType type(Context*); + + virtual void asAssemblerOperand(Context* c UNUSED, Site* high UNUSED, + lir::Operand* result); + + virtual Site* copy(Context* c); + + Site* copyHalf(Context* c, bool add); + + virtual Site* copyLow(Context* c); + + virtual Site* copyHigh(Context* c); + + virtual Site* makeNextWord(Context* c, unsigned index); + + virtual SiteMask mask(Context* c); + + virtual SiteMask nextWordMask(Context* c, unsigned index); + + virtual bool isVolatile(Context* c); + + bool acquired; + int base; + int offset; + int index; + unsigned scale; +}; + +MemorySite* memorySite(Context* c, int base, int offset = 0, int index = lir::NoRegister, unsigned scale = 1); +MemorySite* frameSite(Context* c, int frameIndex); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_SITE_H diff --git a/src/codegen/compiler/value.cpp b/src/codegen/compiler/value.cpp new file mode 100644 index 0000000000..fcfa7d9946 --- /dev/null +++ b/src/codegen/compiler/value.cpp @@ -0,0 +1,164 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "codegen/compiler/regalloc.h" +#include "codegen/compiler/site.h" + +namespace avian { +namespace codegen { +namespace compiler { + +Value::Value(Site* site, Site* target, lir::ValueType type): + reads(0), lastRead(0), sites(site), source(0), target(target), buddy(this), + nextWord(this), home(NoFrameIndex), type(type), wordIndex(0) +{ } + +bool Value::findSite(Site* site) { + for (Site* s = this->sites; s; s = s->next) { + if (s == site) return true; + } + return false; +} + +bool Value::isBuddyOf(Value* b) { + Value* a = this; + if (a == b) return true; + for (Value* p = a->buddy; p != a; p = p->buddy) { + if (p == b) return true; + } + return false; +} + +void Value::addSite(Context* c, Site* s) { + if (not this->findSite(s)) { + if (DebugSites) { + char buffer[256]; s->toString(c, buffer, 256); + fprintf(stderr, "add site %s to %p\n", buffer, this); + } + s->acquire(c, this); + s->next = this->sites; + this->sites = s; + } +} + + +void Value::grow(Context* c) { + assert(c, this->nextWord == this); + + Value* next = value(c, this->type); + this->nextWord = next; + next->nextWord = this; + next->wordIndex = 1; +} + + +void Value::maybeSplit(Context* c) { + if (this->nextWord == this) { + this->split(c); + } +} + +void Value::split(Context* c) { + this->grow(c); + for (SiteIterator it(c, this); it.hasMore();) { + Site* s = it.next(); + this->removeSite(c, s); + + this->addSite(c, s->copyLow(c)); + this->nextWord->addSite(c, s->copyHigh(c)); + } +} + +void Value::removeSite(Context* c, Site* s) { + for (SiteIterator it(c, this); it.hasMore();) { + if (s == it.next()) { + if (DebugSites) { + char buffer[256]; s->toString(c, buffer, 256); + fprintf(stderr, "remove site %s from %p\n", buffer, this); + } + it.remove(c); + break; + } + } + if (DebugSites) { + fprintf(stderr, "%p has more: %d\n", this, this->hasSite(c)); + } + assert(c, not this->findSite(s)); +} + +bool Value::hasSite(Context* c) { + SiteIterator it(c, this); + return it.hasMore(); +} + +bool Value::uniqueSite(Context* c, Site* s) { + SiteIterator it(c, this); + Site* p UNUSED = it.next(); + if (it.hasMore()) { + // the site is not this word's only site, but if the site is + // shared with the next word, it may be that word's only site + if (this->nextWord != this and s->registerSize(c) > vm::TargetBytesPerWord) { + SiteIterator nit(c, this->nextWord); + Site* p = nit.next(); + if (nit.hasMore()) { + return false; + } else { + return p == s; + } + } else { + return false; + } + } else { + assert(c, p == s); + return true; + } +} + +void Value::clearSites(Context* c) { + if (DebugSites) { + fprintf(stderr, "clear sites for %p\n", this); + } + for (SiteIterator it(c, this); it.hasMore();) { + it.next(); + it.remove(c); + } +} + + +#ifndef NDEBUG +bool Value::hasBuddy(Context* c, Value* b) { + Value* a = this; + if (a == b) { + return true; + } + + int i = 0; + for (Value* p = a->buddy; p != a; p = p->buddy) { + if (p == b) { + return true; + } + if (++i > 1000) { + abort(c); + } + } + return false; +} +#endif // not NDEBUG + + +Value* value(Context* c, lir::ValueType type, Site* site, Site* target) { + return new(c->zone) Value(site, target, type); +} + +} // namespace regalloc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/compiler/value.h b/src/codegen/compiler/value.h new file mode 100644 index 0000000000..60fe0461a3 --- /dev/null +++ b/src/codegen/compiler/value.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_COMPILER_VALUE_H +#define AVIAN_CODEGEN_COMPILER_VALUE_H + +#include +#include + +namespace avian { +namespace codegen { +namespace compiler { + +class Read; +class Site; + +const int AnyFrameIndex = -2; +const int NoFrameIndex = -1; + +const bool DebugSites = false; + +class Value: public Compiler::Operand { + public: + Read* reads; + Read* lastRead; + Site* sites; + Site* source; + Site* target; + Value* buddy; + Value* nextWord; + int16_t home; + lir::ValueType type; + uint8_t wordIndex; + + Value(Site* site, Site* target, lir::ValueType type); + + bool findSite(Site* site); + + bool isBuddyOf(Value* b); + + void addSite(Context* c, Site* s); + + void grow(Context* c); + + void maybeSplit(Context* c); + + void split(Context* c); + + void removeSite(Context* c, Site* s); + + bool hasSite(Context* c); + + bool uniqueSite(Context* c, Site* s); + + void clearSites(Context* c); + +#ifndef NDEBUG + bool hasBuddy(Context* c, Value* b); +#endif // not NDEBUG + +}; + + +Value* value(Context* c, lir::ValueType type, Site* site = 0, Site* target = 0); + +} // namespace compiler +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_COMPILER_VALUE_H \ No newline at end of file diff --git a/src/codegen/lir.cpp b/src/codegen/lir.cpp new file mode 100644 index 0000000000..86c0839743 --- /dev/null +++ b/src/codegen/lir.cpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2012, 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 "lir.h" + +namespace { + +const char* lirOpcodeNames[] = { + #define LIR_OP_0(x) #x + #define LIR_OP_1(x) #x + #define LIR_OP_2(x) #x + #define LIR_OP_3(x) #x + #include "lir-ops.inc.cpp" + #undef LIR_OP_0 + #undef LIR_OP_1 + #undef LIR_OP_2 + #undef LIR_OP_3 +}; + +} + +namespace vm { + +const char* LirInstr::opcodeName(Opcode op) { + return lirOpcodeNames[op]; +} + +} \ No newline at end of file diff --git a/src/codegen/registers.cpp b/src/codegen/registers.cpp new file mode 100644 index 0000000000..c3bf451176 --- /dev/null +++ b/src/codegen/registers.cpp @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2012, 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 + +namespace avian { +namespace codegen { + +unsigned +RegisterMask::maskStart(uint32_t mask) +{ + for (int i = 0; i <= 31; ++i) { + if (mask & (1 << i)) return i; + } + return 32; +} + +unsigned +RegisterMask::maskLimit(uint32_t mask) +{ + for (int i = 31; i >= 0; --i) { + if (mask & (1 << i)) return i + 1; + } + return 0; +} + +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/assembler.cpp b/src/codegen/target/arm/assembler.cpp new file mode 100644 index 0000000000..64eda1395a --- /dev/null +++ b/src/codegen/target/arm/assembler.cpp @@ -0,0 +1,962 @@ +/* Copyright (c) 2010-2012, 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 + +#include +#include +#include + +#include "context.h" +#include "block.h" +#include "fixup.h" +#include "multimethod.h" +#include "encode.h" +#include "operations.h" +#include "registers.h" +#include "../multimethod.h" + +#include "avian/alloc-vector.h" +#include + +using namespace vm; +using namespace avian::codegen; +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace arm { + +namespace isa { +// HARDWARE FLAGS +bool vfpSupported() { + // TODO: Use at runtime detection +#if defined(__ARM_PCS_VFP) + // armhf + return true; +#else + // armel + // TODO: allow VFP use for -mfloat-abi=softfp armel builds. + // GCC -mfloat-abi=softfp flag allows use of VFP while remaining compatible + // with soft-float code. + return false; +#endif +} +} // namespace isa + +inline unsigned lo8(int64_t i) { return (unsigned)(i&MASK_LO8); } + +const RegisterFile MyRegisterFileWithoutFloats(GPR_MASK, 0); +const RegisterFile MyRegisterFileWithFloats(GPR_MASK, FPR_MASK); + +const unsigned FrameHeaderSize = 1; + +const unsigned StackAlignmentInBytes = 8; +const unsigned StackAlignmentInWords += StackAlignmentInBytes / TargetBytesPerWord; + +void resolve(MyBlock*); + +unsigned padding(MyBlock*, unsigned); + +class ConstantPoolEntry; + +// BEGIN OPERATION COMPILERS + +using namespace isa; + +// END OPERATION COMPILERS + +unsigned +argumentFootprint(unsigned footprint) +{ + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); +} + +void +nextFrame(ArchitectureContext* con, uint32_t* start, unsigned size UNUSED, + unsigned footprint, void* link, bool, + unsigned targetParameterFootprint UNUSED, void** ip, void** stack) +{ + assert(con, *ip >= start); + assert(con, *ip <= start + (size / TargetBytesPerWord)); + + uint32_t* instruction = static_cast(*ip); + + if ((*start >> 20) == 0xe59) { + // skip stack overflow check + start += 3; + } + + if (instruction <= start) { + *ip = link; + return; + } + + unsigned offset = footprint + FrameHeaderSize; + + if (instruction <= start + 2) { + *ip = link; + *stack = static_cast(*stack) + offset; + return; + } + + if (*instruction == 0xe12fff1e) { // return + *ip = link; + return; + } + + if (TailCalls) { + if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { + offset += argumentFootprint(targetParameterFootprint) + - StackAlignmentInWords; + } + + // check for post-non-tail-call stack adjustment of the form "add + // sp, sp, #offset": + if ((*instruction >> 12) == 0xe24dd) { + unsigned value = *instruction & 0xff; + unsigned rotation = (*instruction >> 8) & 0xf; + switch (rotation) { + case 0: offset -= value / TargetBytesPerWord; break; + case 15: offset -= value; break; + default: abort(con); + } + } + + // todo: check for and handle tail calls + } + + *ip = static_cast(*stack)[offset - 1]; + *stack = static_cast(*stack) + offset; +} + +class MyArchitecture: public Architecture { + public: + MyArchitecture(System* system): con(system), referenceCount(0) { + populateTables(&con); + } + + virtual unsigned floatRegisterSize() { + return vfpSupported() ? 8 : 0; + } + + virtual const RegisterFile* registerFile() { + return vfpSupported() ? &MyRegisterFileWithFloats : &MyRegisterFileWithoutFloats; + } + + virtual int scratch() { + return 5; + } + + virtual int stack() { + return StackRegister; + } + + virtual int thread() { + return ThreadRegister; + } + + virtual int returnLow() { + return 0; + } + + virtual int returnHigh() { + return 1; + } + + virtual int virtualCallTarget() { + return 4; + } + + virtual int virtualCallIndex() { + return 3; + } + + virtual bool bigEndian() { + return false; + } + + virtual uintptr_t maximumImmediateJump() { + return 0x1FFFFFF; + } + + virtual bool reserved(int register_) { + switch (register_) { + case LinkRegister: + case StackRegister: + case ThreadRegister: + case ProgramCounter: + return true; + + default: + return false; + } + } + + virtual unsigned frameFootprint(unsigned footprint) { + return max(footprint, StackAlignmentInWords); + } + + virtual unsigned argumentFootprint(unsigned footprint) { + return arm::argumentFootprint(footprint); + } + + virtual bool argumentAlignment() { +#ifdef __APPLE__ + return false; +#else + return true; +#endif + } + + virtual bool argumentRegisterAlignment() { +#ifdef __APPLE__ + return false; +#else + return true; +#endif + } + + virtual unsigned argumentRegisterCount() { + return 4; + } + + virtual int argumentRegister(unsigned index) { + assert(&con, index < argumentRegisterCount()); + + return index; + } + + virtual bool hasLinkRegister() { + return true; + } + + 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(lir::UnaryOperation op UNUSED, + void* returnAddress, + void* newTarget) + { + switch (op) { + case lir::Call: + case lir::Jump: + case lir::AlignedCall: + case lir::AlignedJump: { + updateOffset(con.s, static_cast(returnAddress) - 4, + reinterpret_cast(newTarget)); + } break; + + case lir::LongCall: + case lir::LongJump: + case lir::AlignedLongCall: + case lir::AlignedLongJump: { + uint32_t* p = static_cast(returnAddress) - 2; + *reinterpret_cast(p + (((*p & PoolOffsetMask) + 8) / 4)) + = newTarget; + } break; + + default: abort(&con); + } + } + + virtual unsigned constantCallSize() { + return 4; + } + + virtual void setConstant(void* dst, uint64_t constant) { + *static_cast(dst) = constant; + } + + virtual unsigned alignFrameSize(unsigned sizeInWords) { + return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) + - FrameHeaderSize; + } + + virtual void nextFrame(void* start, unsigned size, unsigned footprint, + void* link, bool mostRecent, + unsigned targetParameterFootprint, void** ip, + void** stack) + { + arm::nextFrame(&con, static_cast(start), size, footprint, link, + mostRecent, targetParameterFootprint, ip, stack); + } + + virtual void* frameIp(void* stack) { + return stack ? static_cast(stack)[returnAddressOffset()] : 0; + } + + virtual unsigned frameHeaderSize() { + return FrameHeaderSize; + } + + virtual unsigned frameReturnAddressSize() { + return 0; + } + + virtual unsigned frameFooterSize() { + return 0; + } + + virtual int returnAddressOffset() { + return -1; + } + + virtual int framePointerOffset() { + return 0; + } + + virtual bool alwaysCondensed(lir::BinaryOperation) { + return false; + } + + virtual bool alwaysCondensed(lir::TernaryOperation) { + return false; + } + + virtual void plan + (lir::UnaryOperation, + unsigned, OperandMask& aMask, + bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + aMask.registerMask = ~static_cast(0); + *thunk = false; + } + + virtual void planSource + (lir::BinaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned bSize, bool* thunk) + { + *thunk = false; + aMask.typeMask = ~0; + aMask.registerMask = GPR_MASK64; + + switch (op) { + case lir::Negate: + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = GPR_MASK64; + break; + + case lir::Absolute: + *thunk = true; + break; + + case lir::FloatAbsolute: + case lir::FloatSquareRoot: + case lir::FloatNegate: + case lir::Float2Float: + if (vfpSupported()) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = FPR_MASK64; + } else { + *thunk = true; + } + break; + + case lir::Float2Int: + // todo: Java requires different semantics than SSE for + // converting floats to integers, we we need to either use + // thunks or produce inline machine code which handles edge + // cases properly. + if (false && vfpSupported() && bSize == 4) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = FPR_MASK64; + } else { + *thunk = true; + } + break; + + case lir::Int2Float: + if (vfpSupported() && aSize == 4) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = GPR_MASK64; + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination + (lir::BinaryOperation op, + unsigned, const OperandMask& aMask, + unsigned, OperandMask& bMask) + { + bMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + bMask.registerMask = GPR_MASK64; + + switch (op) { + case lir::Negate: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = GPR_MASK64; + break; + + case lir::FloatAbsolute: + case lir::FloatSquareRoot: + case lir::FloatNegate: + case lir::Float2Float: + case lir::Int2Float: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = FPR_MASK64; + break; + + case lir::Float2Int: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = GPR_MASK64; + break; + + case lir::Move: + if (!(aMask.typeMask & 1 << lir::RegisterOperand)) { + bMask.typeMask = 1 << lir::RegisterOperand; + } + break; + + default: + break; + } + } + + virtual void planMove + (unsigned, OperandMask& srcMask, + OperandMask& tmpMask, + const OperandMask& dstMask) + { + srcMask.typeMask = ~0; + srcMask.registerMask = ~static_cast(0); + + tmpMask.typeMask = 0; + tmpMask.registerMask = 0; + + if (dstMask.typeMask & (1 << lir::MemoryOperand)) { + // can't move directly from memory or constant to memory + srcMask.typeMask = 1 << lir::RegisterOperand; + tmpMask.typeMask = 1 << lir::RegisterOperand; + tmpMask.registerMask = GPR_MASK64; + } else if (vfpSupported() && + dstMask.typeMask & 1 << lir::RegisterOperand && + dstMask.registerMask & FPR_MASK) { + srcMask.typeMask = tmpMask.typeMask = 1 << lir::RegisterOperand | + 1 << lir::MemoryOperand; + tmpMask.registerMask = ~static_cast(0); + } + } + + virtual void planSource + (lir::TernaryOperation op, + unsigned, OperandMask& aMask, + unsigned bSize, OperandMask& bMask, + unsigned, bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + aMask.registerMask = GPR_MASK64; + + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = GPR_MASK64; + + *thunk = false; + + switch (op) { + case lir::ShiftLeft: + case lir::ShiftRight: + case lir::UnsignedShiftRight: + if (bSize == 8) aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + break; + + case lir::Add: + case lir::Subtract: + case lir::Or: + case lir::Xor: + case lir::Multiply: + aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + break; + + case lir::Divide: + case lir::Remainder: + case lir::FloatRemainder: + *thunk = true; + break; + + case lir::FloatAdd: + case lir::FloatSubtract: + case lir::FloatMultiply: + case lir::FloatDivide: + if (vfpSupported()) { + aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = bMask.registerMask = FPR_MASK64; + } else { + *thunk = true; + } + break; + + case lir::JumpIfFloatEqual: + case lir::JumpIfFloatNotEqual: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatLessOrUnordered: + case lir::JumpIfFloatGreaterOrUnordered: + case lir::JumpIfFloatLessOrEqualOrUnordered: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + if (vfpSupported()) { + aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = bMask.registerMask = FPR_MASK64; + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination + (lir::TernaryOperation op, + unsigned, const OperandMask& aMask UNUSED, + unsigned, const OperandMask& bMask, + unsigned, OperandMask& cMask) + { + if (isBranch(op)) { + cMask.typeMask = (1 << lir::ConstantOperand); + cMask.registerMask = 0; + } else { + cMask.typeMask = (1 << lir::RegisterOperand); + cMask.registerMask = bMask.registerMask; + } + } + + virtual Assembler* makeAssembler(Allocator* allocator, Zone* zone); + + virtual void acquire() { + ++ referenceCount; + } + + virtual void release() { + if (-- referenceCount == 0) { + con.s->free(this); + } + } + + ArchitectureContext con; + unsigned referenceCount; +}; + +class MyAssembler: public Assembler { + public: + MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): + con(s, a, zone), arch_(arch) + { } + + virtual void setClient(Client* client) { + assert(&con, con.client == 0); + con.client = client; + } + + virtual Architecture* arch() { + return arch_; + } + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) + { + lir::Register stack(StackRegister); + lir::Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); + lir::Constant handlerConstant(new(con.zone) ResolvedPromise(handler)); + branchRM(&con, lir::JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, + &handlerConstant); + } + + virtual void saveFrame(unsigned stackOffset, unsigned ipOffset) { + lir::Register link(LinkRegister); + lir::Memory linkDst(ThreadRegister, ipOffset); + moveRM(&con, TargetBytesPerWord, &link, TargetBytesPerWord, &linkDst); + + lir::Register stack(StackRegister); + lir::Memory stackDst(ThreadRegister, stackOffset); + moveRM(&con, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); + } + + virtual void pushFrame(unsigned argumentCount, ...) { + struct Argument { + unsigned size; + lir::OperandType type; + lir::Operand* operand; + }; + RUNTIME_ARRAY(Argument, arguments, argumentCount); + + va_list a; va_start(a, argumentCount); + unsigned footprint = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned); + RUNTIME_ARRAY_BODY(arguments)[i].type = static_cast(va_arg(a, int)); + RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, lir::Operand*); + footprint += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); + } + va_end(a); + + allocateFrame(arch_->alignFrameSize(footprint)); + + unsigned offset = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + if (i < arch_->argumentRegisterCount()) { + lir::Register dst(arch_->argumentRegister(i)); + + apply(lir::Move, + OperandInfo( + RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo( + pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), lir::RegisterOperand, &dst)); + + offset += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); + } else { + lir::Memory dst(StackRegister, offset * TargetBytesPerWord); + + apply(lir::Move, + OperandInfo( + RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo( + pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), lir::MemoryOperand, &dst)); + + offset += ceilingDivide(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); + } + } + } + + virtual void allocateFrame(unsigned footprint) { + footprint += FrameHeaderSize; + + // larger frames may require multiple subtract/add instructions + // to allocate/deallocate, and nextFrame will need to be taught + // how to handle them: + assert(&con, footprint < 256); + + lir::Register stack(StackRegister); + ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + subC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + + lir::Register returnAddress(LinkRegister); + lir::Memory returnAddressDst + (StackRegister, (footprint - 1) * TargetBytesPerWord); + moveRM(&con, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, + &returnAddressDst); + } + + virtual void adjustFrame(unsigned difference) { + lir::Register stack(StackRegister); + ResolvedPromise differencePromise(difference * TargetBytesPerWord); + lir::Constant differenceConstant(&differencePromise); + subC(&con, TargetBytesPerWord, &differenceConstant, &stack, &stack); + } + + virtual void popFrame(unsigned footprint) { + footprint += FrameHeaderSize; + + lir::Register returnAddress(LinkRegister); + lir::Memory returnAddressSrc + (StackRegister, (footprint - 1) * TargetBytesPerWord); + moveMR(&con, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, + &returnAddress); + + lir::Register stack(StackRegister); + ResolvedPromise footprintPromise(footprint * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + } + + virtual void popFrameForTailCall(unsigned footprint, + int offset, + int returnAddressSurrogate, + int framePointerSurrogate UNUSED) + { + assert(&con, framePointerSurrogate == lir::NoRegister); + + if (TailCalls) { + if (offset) { + footprint += FrameHeaderSize; + + lir::Register link(LinkRegister); + lir::Memory returnAddressSrc + (StackRegister, (footprint - 1) * TargetBytesPerWord); + moveMR(&con, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, + &link); + + lir::Register stack(StackRegister); + ResolvedPromise footprintPromise + ((footprint - offset) * TargetBytesPerWord); + lir::Constant footprintConstant(&footprintPromise); + addC(&con, TargetBytesPerWord, &footprintConstant, &stack, &stack); + + if (returnAddressSurrogate != lir::NoRegister) { + assert(&con, offset > 0); + + lir::Register ras(returnAddressSurrogate); + lir::Memory dst(StackRegister, (offset - 1) * TargetBytesPerWord); + moveRM(&con, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); + } + } else { + popFrame(footprint); + } + } else { + abort(&con); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + { + popFrame(frameFootprint); + + assert(&con, argumentFootprint >= StackAlignmentInWords); + assert(&con, (argumentFootprint % StackAlignmentInWords) == 0); + + unsigned offset; + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + offset = argumentFootprint - StackAlignmentInWords; + + lir::Register stack(StackRegister); + ResolvedPromise adjustmentPromise(offset * TargetBytesPerWord); + lir::Constant adjustment(&adjustmentPromise); + addC(&con, TargetBytesPerWord, &adjustment, &stack, &stack); + } else { + offset = 0; + } + + return_(&con); + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + { + popFrame(frameFootprint); + + lir::Register stack(StackRegister); + lir::Memory newStackSrc(ThreadRegister, stackOffsetFromThread); + moveMR(&con, TargetBytesPerWord, &newStackSrc, TargetBytesPerWord, &stack); + + return_(&con); + } + + virtual void apply(lir::Operation op) { + arch_->con.operations[op](&con); + } + + virtual void apply(lir::UnaryOperation op, OperandInfo a) + { + arch_->con.unaryOperations[Multimethod::index(op, a.type)] + (&con, a.size, a.operand); + } + + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) + { + arch_->con.binaryOperations[index(&(arch_->con), op, a.type, b.type)] + (&con, a.size, a.operand, b.size, b.operand); + } + + virtual void apply(lir::TernaryOperation op, OperandInfo a, OperandInfo b, OperandInfo c) + { + if (isBranch(op)) { + assert(&con, a.size == b.size); + assert(&con, c.size == TargetBytesPerWord); + assert(&con, c.type == lir::ConstantOperand); + + arch_->con.branchOperations[branchIndex(&(arch_->con), a.type, b.type)] + (&con, op, a.size, a.operand, b.operand, c.operand); + } else { + assert(&con, b.size == c.size); + assert(&con, b.type == lir::RegisterOperand); + assert(&con, c.type == lir::RegisterOperand); + + arch_->con.ternaryOperations[index(&(arch_->con), op, a.type)] + (&con, b.size, a.operand, b.operand, c.operand); + } + } + + virtual void setDestination(uint8_t* dst) { + con.result = dst; + } + + virtual void write() { + uint8_t* dst = con.result; + unsigned dstOffset = 0; + for (MyBlock* b = con.firstBlock; b; b = b->next) { + if (DebugPool) { + fprintf(stderr, "write block %p\n", b); + } + + unsigned blockOffset = 0; + for (PoolEvent* e = b->poolEventHead; e; e = e->next) { + unsigned size = e->offset - blockOffset; + memcpy(dst + dstOffset, con.code.data + b->offset + blockOffset, size); + blockOffset = e->offset; + dstOffset += size; + + unsigned poolSize = 0; + for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { + if (DebugPool) { + fprintf(stderr, "visit pool offset %p %d in block %p\n", + o, o->offset, b); + } + + unsigned entry = dstOffset + poolSize; + + if (needJump(b)) { + entry += TargetBytesPerWord; + } + + o->entry->address = dst + entry; + + unsigned instruction = o->block->start + + padding(o->block, o->offset) + o->offset; + + int32_t v = (entry - 8) - instruction; + expect(&con, v == (v & PoolOffsetMask)); + + int32_t* p = reinterpret_cast(dst + instruction); + *p = (v & PoolOffsetMask) | ((~PoolOffsetMask) & *p); + + poolSize += TargetBytesPerWord; + } + + bool jump = needJump(b); + if (jump) { + write4 + (dst + dstOffset, isa::b((poolSize + TargetBytesPerWord - 8) >> 2)); + } + + dstOffset += poolSize + (jump ? TargetBytesPerWord : 0); + } + + unsigned size = b->size - blockOffset; + + memcpy(dst + dstOffset, + con.code.data + b->offset + blockOffset, + size); + + dstOffset += size; + } + + for (Task* t = con.tasks; t; t = t->next) { + t->run(&con); + } + + for (ConstantPoolEntry* e = con.constantPool; e; e = e->next) { + if (e->constant->resolved()) { + *static_cast(e->address) = e->constant->value(); + } else { + new (e->constant->listen(sizeof(ConstantPoolListener))) + ConstantPoolListener(con.s, static_cast(e->address), + e->callOffset + ? dst + e->callOffset->value() + 8 + : 0); + } +// fprintf(stderr, "constant %p at %p\n", reinterpret_cast(e->constant->value()), e->address); + } + } + + virtual Promise* offset(bool forTrace) { + return arm::offsetPromise(&con, forTrace); + } + + virtual Block* endBlock(bool startNew) { + MyBlock* b = con.lastBlock; + b->size = con.code.length() - b->offset; + if (startNew) { + con.lastBlock = new (con.zone) MyBlock(&con, con.code.length()); + } else { + con.lastBlock = 0; + } + return b; + } + + virtual void endEvent() { + MyBlock* b = con.lastBlock; + unsigned thisEventOffset = con.code.length() - b->offset; + if (b->poolOffsetHead) { + int32_t v = (thisEventOffset + TargetBytesPerWord - 8) + - b->poolOffsetHead->offset; + + if (v > 0 and v != (v & PoolOffsetMask)) { + appendPoolEvent + (&con, b, b->lastEventOffset, b->poolOffsetHead, + b->lastPoolOffsetTail); + + if (DebugPool) { + for (PoolOffset* o = b->poolOffsetHead; + o != b->lastPoolOffsetTail->next; o = o->next) + { + fprintf(stderr, + "in endEvent, include %p %d in pool event %p at offset %d " + "in block %p\n", + o, o->offset, b->poolEventTail, b->lastEventOffset, b); + } + } + + b->poolOffsetHead = b->lastPoolOffsetTail->next; + b->lastPoolOffsetTail->next = 0; + if (b->poolOffsetHead == 0) { + b->poolOffsetTail = 0; + } + } + } + b->lastEventOffset = thisEventOffset; + b->lastPoolOffsetTail = b->poolOffsetTail; + } + + virtual unsigned length() { + return con.code.length(); + } + + virtual unsigned footerSize() { + return 0; + } + + virtual void dispose() { + con.code.dispose(); + } + + Context con; + MyArchitecture* arch_; +}; + +Assembler* MyArchitecture::makeAssembler(Allocator* allocator, Zone* zone) { + return new(zone) MyAssembler(this->con.s, allocator, zone, this); +} + +} // namespace arm + +Architecture* +makeArchitectureArm(System* system, bool) +{ + return new (allocate(system, sizeof(arm::MyArchitecture))) arm::MyArchitecture(system); +} + +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/block.cpp b/src/codegen/target/arm/block.cpp new file mode 100644 index 0000000000..7216e86f55 --- /dev/null +++ b/src/codegen/target/arm/block.cpp @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace arm { + +void resolve(MyBlock*); + +unsigned padding(MyBlock*, unsigned); + +MyBlock::MyBlock(Context* context, unsigned offset): + context(context), next(0), poolOffsetHead(0), poolOffsetTail(0), + lastPoolOffsetTail(0), poolEventHead(0), poolEventTail(0), + lastEventOffset(0), offset(offset), start(~0), size(0) +{ } + +unsigned MyBlock::resolve(unsigned start, Assembler::Block* next) { + this->start = start; + this->next = static_cast(next); + + arm::resolve(this); + + return start + size + padding(this, size); +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/block.h b/src/codegen/target/arm/block.h new file mode 100644 index 0000000000..cc634f7f75 --- /dev/null +++ b/src/codegen/target/arm/block.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H + +#include +#include + +namespace avian { +namespace codegen { +namespace arm { + +class PoolEvent; + +class MyBlock: public Assembler::Block { + public: + MyBlock(Context* context, unsigned offset); + + virtual unsigned resolve(unsigned start, Assembler::Block* next); + + Context* context; + MyBlock* next; + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + PoolOffset* lastPoolOffsetTail; + PoolEvent* poolEventHead; + PoolEvent* poolEventTail; + unsigned lastEventOffset; + unsigned offset; + unsigned start; + unsigned size; +}; + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_BLOCK_H diff --git a/src/codegen/target/arm/context.cpp b/src/codegen/target/arm/context.cpp new file mode 100644 index 0000000000..d3619adf85 --- /dev/null +++ b/src/codegen/target/arm/context.cpp @@ -0,0 +1,27 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace arm { + +Context::Context(vm::System* s, vm::Allocator* a, vm::Zone* zone): + s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), + firstBlock(new(zone) MyBlock(this, 0)), + lastBlock(firstBlock), poolOffsetHead(0), poolOffsetTail(0), + constantPool(0), constantPoolCount(0) +{ } + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/context.h b/src/codegen/target/arm/context.h new file mode 100644 index 0000000000..57d25659fa --- /dev/null +++ b/src/codegen/target/arm/context.h @@ -0,0 +1,99 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H + +#include +#include +#include "avian/alloc-vector.h" + +namespace vm { +class System; +class Allocator; +class Zone; +} // namespace vm + +namespace avian { + +namespace util { +class Aborter; +} // namespace util + +namespace codegen { +namespace arm { + +class Task; +class MyBlock; +class PoolOffset; +class ConstantPoolEntry; + +class Context { + public: + Context(vm::System* s, vm::Allocator* a, vm::Zone* zone); + + vm::System* s; + vm::Zone* zone; + Assembler::Client* client; + vm::Vector code; + Task* tasks; + uint8_t* result; + MyBlock* firstBlock; + MyBlock* lastBlock; + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + ConstantPoolEntry* constantPool; + unsigned constantPoolCount; +}; + +typedef void (*OperationType)(Context*); + +typedef void (*UnaryOperationType)(Context*, unsigned, lir::Operand*); + +typedef void (*BinaryOperationType) +(Context*, unsigned, lir::Operand*, unsigned, lir::Operand*); + +typedef void (*TernaryOperationType) +(Context*, unsigned, lir::Operand*, lir::Operand*, + lir::Operand*); + +typedef void (*BranchOperationType) +(Context*, lir::TernaryOperation, unsigned, lir::Operand*, + lir::Operand*, lir::Operand*); + +class ArchitectureContext { + public: + ArchitectureContext(vm::System* s): s(s) { } + + vm::System* s; + OperationType operations[lir::OperationCount]; + UnaryOperationType unaryOperations[lir::UnaryOperationCount + * lir::OperandTypeCount]; + BinaryOperationType binaryOperations + [lir::BinaryOperationCount * lir::OperandTypeCount * lir::OperandTypeCount]; + TernaryOperationType ternaryOperations + [lir::NonBranchTernaryOperationCount * lir::OperandTypeCount]; + BranchOperationType branchOperations + [lir::BranchOperationCount * lir::OperandTypeCount * lir::OperandTypeCount]; +}; + +inline avian::util::Aborter* getAborter(Context* c) { + return c->s; +} + +inline avian::util::Aborter* getAborter(ArchitectureContext* c) { + return c->s; +} + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_CONTEXT_H diff --git a/src/codegen/target/arm/encode.h b/src/codegen/target/arm/encode.h new file mode 100644 index 0000000000..d6d3e983b9 --- /dev/null +++ b/src/codegen/target/arm/encode.h @@ -0,0 +1,184 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H + +#include +#include + +namespace avian { +namespace codegen { +namespace arm { + +namespace isa { + +// SYSTEM REGISTERS +const int FPSID = 0x0; +const int FPSCR = 0x1; +const int FPEXC = 0x8; +// INSTRUCTION OPTIONS +enum CONDITION { EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV }; +enum SHIFTOP { LSL, LSR, ASR, ROR }; +// INSTRUCTION FORMATS +inline int DATA(int cond, int opcode, int S, int Rn, int Rd, int shift, int Sh, int Rm) +{ return cond<<28 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | shift<<7 | Sh<<5 | Rm; } +inline int DATAS(int cond, int opcode, int S, int Rn, int Rd, int Rs, int Sh, int Rm) +{ return cond<<28 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | Rs<<8 | Sh<<5 | 1<<4 | Rm; } +inline int DATAI(int cond, int opcode, int S, int Rn, int Rd, int rot, int imm) +{ return cond<<28 | 1<<25 | opcode<<21 | S<<20 | Rn<<16 | Rd<<12 | rot<<8 | (imm&0xff); } +inline int BRANCH(int cond, int L, int offset) +{ return cond<<28 | 5<<25 | L<<24 | (offset&0xffffff); } +inline int BRANCHX(int cond, int L, int Rm) +{ return cond<<28 | 0x4bffc<<6 | L<<5 | 1<<4 | Rm; } +inline int MULTIPLY(int cond, int mul, int S, int Rd, int Rn, int Rs, int Rm) +{ return cond<<28 | mul<<21 | S<<20 | Rd<<16 | Rn<<12 | Rs<<8 | 9<<4 | Rm; } +inline int XFER(int cond, int P, int U, int B, int W, int L, int Rn, int Rd, int shift, int Sh, int Rm) +{ return cond<<28 | 3<<25 | P<<24 | U<<23 | B<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | shift<<7 | Sh<<5 | Rm; } +inline int XFERI(int cond, int P, int U, int B, int W, int L, int Rn, int Rd, int offset) +{ return cond<<28 | 2<<25 | P<<24 | U<<23 | B<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | (offset&0xfff); } +inline int XFER2(int cond, int P, int U, int W, int L, int Rn, int Rd, int S, int H, int Rm) +{ return cond<<28 | P<<24 | U<<23 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | 1<<7 | S<<6 | H<<5 | 1<<4 | Rm; } +inline int XFER2I(int cond, int P, int U, int W, int L, int Rn, int Rd, int offsetH, int S, int H, int offsetL) +{ return cond<<28 | P<<24 | U<<23 | 1<<22 | W<<21 | L<<20 | Rn<<16 | Rd<<12 | offsetH<<8 | 1<<7 | S<<6 | H<<5 | 1<<4 | (offsetL&0xf); } +inline int COOP(int cond, int opcode_1, int CRn, int CRd, int cp_num, int opcode_2, int CRm) +{ return cond<<28 | 0xe<<24 | opcode_1<<20 | CRn<<16 | CRd<<12 | cp_num<<8 | opcode_2<<5 | CRm; } +inline int COXFER(int cond, int P, int U, int N, int W, int L, int Rn, int CRd, int cp_num, int offset) // offset is in words, not bytes +{ return cond<<28 | 0x6<<25 | P<<24 | U<<23 | N<<22 | W<<21 | L<<20 | Rn<<16 | CRd<<12 | cp_num<<8 | (offset&0xff)>>2; } +inline int COREG(int cond, int opcode_1, int L, int CRn, int Rd, int cp_num, int opcode_2, int CRm) +{ return cond<<28 | 0xe<<24 | opcode_1<<21 | L<<20 | CRn<<16 | Rd<<12 | cp_num<<8 | opcode_2<<5 | 1<<4 | CRm; } +inline int COREG2(int cond, int L, int Rn, int Rd, int cp_num, int opcode, int CRm) +{ return cond<<28 | 0xc4<<20 | L<<20 | Rn<<16 | Rd<<12 | cp_num<<8 | opcode<<4 | CRm;} +// FIELD CALCULATORS +inline int calcU(int imm) { return imm >= 0 ? 1 : 0; } +// INSTRUCTIONS +// The "cond" and "S" fields are set using the SETCOND() and SETS() functions +inline int b(int offset) { return BRANCH(AL, 0, offset); } +inline int bl(int offset) { return BRANCH(AL, 1, offset); } +inline int bx(int Rm) { return BRANCHX(AL, 0, Rm); } +inline int blx(int Rm) { return BRANCHX(AL, 1, Rm); } +inline int and_(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x0, 0, Rn, Rd, shift, Sh, Rm); } +inline int eor(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x1, 0, Rn, Rd, shift, Sh, Rm); } +inline int rsb(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x3, 0, Rn, Rd, shift, Sh, Rm); } +inline int add(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x4, 0, Rn, Rd, shift, Sh, Rm); } +inline int adc(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x5, 0, Rn, Rd, shift, Sh, Rm); } +inline int rsc(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0x7, 0, Rn, Rd, shift, Sh, Rm); } +inline int cmp(int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xa, 1, Rn, 0, shift, Sh, Rm); } +inline int orr(int Rd, int Rn, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xc, 0, Rn, Rd, shift, Sh, Rm); } +inline int mov(int Rd, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xd, 0, 0, Rd, shift, Sh, Rm); } +inline int mvn(int Rd, int Rm, int Sh=0, int shift=0) { return DATA(AL, 0xf, 0, 0, Rd, shift, Sh, Rm); } +inline int andi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x0, 0, Rn, Rd, rot, imm); } +inline int subi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x2, 0, Rn, Rd, rot, imm); } +inline int rsbi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x3, 0, Rn, Rd, rot, imm); } +inline int addi(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x4, 0, Rn, Rd, rot, imm); } +inline int adci(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0x5, 0, Rn, Rd, rot, imm); } +inline int bici(int Rd, int Rn, int imm, int rot=0) { return DATAI(AL, 0xe, 0, Rn, Rd, rot, imm); } +inline int cmpi(int Rn, int imm, int rot=0) { return DATAI(AL, 0xa, 1, Rn, 0, rot, imm); } +inline int movi(int Rd, int imm, int rot=0) { return DATAI(AL, 0xd, 0, 0, Rd, rot, imm); } +inline int orrsh(int Rd, int Rn, int Rm, int Rs, int Sh) { return DATAS(AL, 0xc, 0, Rn, Rd, Rs, Sh, Rm); } +inline int movsh(int Rd, int Rm, int Rs, int Sh) { return DATAS(AL, 0xd, 0, 0, Rd, Rs, Sh, Rm); } +inline int mul(int Rd, int Rm, int Rs) { return MULTIPLY(AL, 0, 0, Rd, 0, Rs, Rm); } +inline int mla(int Rd, int Rm, int Rs, int Rn) { return MULTIPLY(AL, 1, 0, Rd, Rn, Rs, Rm); } +inline int umull(int RdLo, int RdHi, int Rm, int Rs) { return MULTIPLY(AL, 4, 0, RdHi, RdLo, Rs, Rm); } +inline int ldr(int Rd, int Rn, int Rm, int W=0) { return XFER(AL, 1, 1, 0, W, 1, Rn, Rd, 0, 0, Rm); } +inline int ldri(int Rd, int Rn, int imm, int W=0) { return XFERI(AL, 1, calcU(imm), 0, W, 1, Rn, Rd, abs(imm)); } +inline int ldrb(int Rd, int Rn, int Rm) { return XFER(AL, 1, 1, 1, 0, 1, Rn, Rd, 0, 0, Rm); } +inline int ldrbi(int Rd, int Rn, int imm) { return XFERI(AL, 1, calcU(imm), 1, 0, 1, Rn, Rd, abs(imm)); } +inline int str(int Rd, int Rn, int Rm, int W=0) { return XFER(AL, 1, 1, 0, W, 0, Rn, Rd, 0, 0, Rm); } +inline int stri(int Rd, int Rn, int imm, int W=0) { return XFERI(AL, 1, calcU(imm), 0, W, 0, Rn, Rd, abs(imm)); } +inline int strb(int Rd, int Rn, int Rm) { return XFER(AL, 1, 1, 1, 0, 0, Rn, Rd, 0, 0, Rm); } +inline int strbi(int Rd, int Rn, int imm) { return XFERI(AL, 1, calcU(imm), 1, 0, 0, Rn, Rd, abs(imm)); } +inline int ldrh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 0, 1, Rm); } +inline int ldrhi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 0, 1, abs(imm)&0xf); } +inline int strh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 0, Rn, Rd, 0, 1, Rm); } +inline int strhi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 0, Rn, Rd, abs(imm)>>4 & 0xf, 0, 1, abs(imm)&0xf); } +inline int ldrsh(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 1, Rm); } +inline int ldrshi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 1, 1, abs(imm)&0xf); } +inline int ldrsb(int Rd, int Rn, int Rm) { return XFER2(AL, 1, 1, 0, 1, Rn, Rd, 1, 0, Rm); } +inline int ldrsbi(int Rd, int Rn, int imm) { return XFER2I(AL, 1, calcU(imm), 0, 1, Rn, Rd, abs(imm)>>4 & 0xf, 1, 0, abs(imm)&0xf); } +// breakpoint instruction, this really has its own instruction format +inline int bkpt(int16_t immed) { return 0xe1200070 | (((unsigned)immed & 0xffff) >> 4 << 8) | (immed & 0xf); } +// COPROCESSOR INSTRUCTIONS +inline int mcr(int coproc, int opcode_1, int Rd, int CRn, int CRm, int opcode_2=0) { return COREG(AL, opcode_1, 0, CRn, Rd, coproc, opcode_2, CRm); } +inline int mcrr(int coproc, int opcode, int Rd, int Rn, int CRm) { return COREG2(AL, 0, Rn, Rd, coproc, opcode, CRm); } +inline int mrc(int coproc, int opcode_1, int Rd, int CRn, int CRm, int opcode_2=0) { return COREG(AL, opcode_1, 1, CRn, Rd, coproc, opcode_2, CRm); } +inline int mrrc(int coproc, int opcode, int Rd, int Rn, int CRm) { return COREG2(AL, 1, Rn, Rd, coproc, opcode, CRm); } +// VFP FLOATING-POINT INSTRUCTIONS +inline int fmuls(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|2, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } +inline int fadds(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|3, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } +inline int fsubs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|3, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1)|2, Sm>>1); } +inline int fdivs(int Sd, int Sn, int Sm) { return COOP(AL, (Sd&1)<<2|8, Sn>>1, Sd>>1, 10, (Sn&1)<<2|(Sm&1), Sm>>1); } +inline int fmuld(int Dd, int Dn, int Dm) { return COOP(AL, 2, Dn, Dd, 11, 0, Dm); } +inline int faddd(int Dd, int Dn, int Dm) { return COOP(AL, 3, Dn, Dd, 11, 0, Dm); } +inline int fsubd(int Dd, int Dn, int Dm) { return COOP(AL, 3, Dn, Dd, 11, 2, Dm); } +inline int fdivd(int Dd, int Dn, int Dm) { return COOP(AL, 8, Dn, Dd, 11, 0, Dm); } +inline int fcpys(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0, Sd>>1, 10, 2|(Sm&1), Sm>>1); } +inline int fabss(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0, Sd>>1, 10, 6|(Sm&1), Sm>>1); } +inline int fnegs(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 1, Sd>>1, 10, 2|(Sm&1), Sm>>1); } +inline int fsqrts(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 1, Sd>>1, 10, 6|(Sm&1), Sm>>1); } +inline int fcmps(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 4, Sd>>1, 10, 2|(Sm&1), Sm>>1); } +inline int fcvtds(int Dd, int Sm) { return COOP(AL, 0xb, 7, Dd, 10, 6|(Sm&1), Sm>>1); } +inline int fsitos(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 8, Sd>>1, 10, 6|(Sm&1), Sm>>1); } +inline int ftosizs(int Sd, int Sm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 10, 6|(Sm&1), Sm>>1); } +inline int fcpyd(int Dd, int Dm) { return COOP(AL, 0xb, 0, Dd, 11, 2, Dm); } +inline int fabsd(int Dd, int Dm) { return COOP(AL, 0xb, 0, Dd, 11, 6, Dm); } +inline int fnegd(int Dd, int Dm) { return COOP(AL, 0xb, 1, Dd, 11, 2, Dm); } +inline int fsqrtd(int Dd, int Dm) { return COOP(AL, 0xb, 1, Dd, 11, 6, Dm); } +// double-precision comparison instructions +inline int fcmpd(int Dd, int Dm) { return COOP(AL, 0xb, 4, Dd, 11, 2, Dm); } +// double-precision conversion instructions +inline int fcvtsd(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 7, Sd>>1, 11, 6, Dm); } +inline int fsitod(int Dd, int Sm) { return COOP(AL, 0xb, 8, Dd, 11, 6|(Sm&1), Sm>>1); } +inline int ftosizd(int Sd, int Dm) { return COOP(AL, 0xb|(Sd&1)<<2, 0xd, Sd>>1, 11, 6, Dm); } +// single load/store instructions for both precision types +inline int flds(int Sd, int Rn, int offset=0) { return COXFER(AL, 1, 1, Sd&1, 0, 1, Rn, Sd>>1, 10, offset); }; +inline int fldd(int Dd, int Rn, int offset=0) { return COXFER(AL, 1, 1, 0, 0, 1, Rn, Dd, 11, offset); }; +inline int fsts(int Sd, int Rn, int offset=0) { return COXFER(AL, 1, 1, Sd&1, 0, 0, Rn, Sd>>1, 10, offset); }; +inline int fstd(int Dd, int Rn, int offset=0) { return COXFER(AL, 1, 1, 0, 0, 0, Rn, Dd, 11, offset); }; +// move between GPRs and FPRs +inline int fmsr(int Sn, int Rd) { return mcr(10, 0, Rd, Sn>>1, 0, (Sn&1)<<2); } +inline int fmrs(int Rd, int Sn) { return mrc(10, 0, Rd, Sn>>1, 0, (Sn&1)<<2); } +// move to/from VFP system registers +inline int fmrx(int Rd, int reg) { return mrc(10, 7, Rd, reg, 0); } +// these move around pairs of single-precision registers +inline int fmdrr(int Dm, int Rd, int Rn) { return mcrr(11, 1, Rd, Rn, Dm); } +inline int fmrrd(int Rd, int Rn, int Dm) { return mrrc(11, 1, Rd, Rn, Dm); } +// FLAG SETTERS +inline int SETCOND(int ins, int cond) { return ((ins&0x0fffffff) | (cond<<28)); } +inline int SETS(int ins) { return ins | 1<<20; } +// PSEUDO-INSTRUCTIONS +inline int lsl(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, LSL); } +inline int lsli(int Rd, int Rm, int imm) { return mov(Rd, Rm, LSL, imm); } +inline int lsr(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, LSR); } +inline int lsri(int Rd, int Rm, int imm) { return mov(Rd, Rm, LSR, imm); } +inline int asr(int Rd, int Rm, int Rs) { return movsh(Rd, Rm, Rs, ASR); } +inline int asri(int Rd, int Rm, int imm) { return mov(Rd, Rm, ASR, imm); } +inline int beq(int offset) { return SETCOND(b(offset), EQ); } +inline int bne(int offset) { return SETCOND(b(offset), NE); } +inline int bls(int offset) { return SETCOND(b(offset), LS); } +inline int bhi(int offset) { return SETCOND(b(offset), HI); } +inline int blt(int offset) { return SETCOND(b(offset), LT); } +inline int bgt(int offset) { return SETCOND(b(offset), GT); } +inline int ble(int offset) { return SETCOND(b(offset), LE); } +inline int bge(int offset) { return SETCOND(b(offset), GE); } +inline int blo(int offset) { return SETCOND(b(offset), CC); } +inline int bhs(int offset) { return SETCOND(b(offset), CS); } +inline int bpl(int offset) { return SETCOND(b(offset), PL); } +inline int fmstat() { return fmrx(15, FPSCR); } + +} // namespace isa + +inline void emit(Context* con, int code) { con->code.append4(code); } + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_ENCODE_H diff --git a/src/codegen/target/arm/fixup.cpp b/src/codegen/target/arm/fixup.cpp new file mode 100644 index 0000000000..88b5789f5a --- /dev/null +++ b/src/codegen/target/arm/fixup.cpp @@ -0,0 +1,177 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "fixup.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace arm { + +using namespace util; + +unsigned padding(MyBlock*, unsigned); + +OffsetPromise::OffsetPromise(Context* con, MyBlock* block, unsigned offset, bool forTrace): + con(con), block(block), offset(offset), forTrace(forTrace) +{ } + +bool OffsetPromise::resolved() { + return block->start != static_cast(~0); +} + +int64_t OffsetPromise::value() { + assert(con, resolved()); + + unsigned o = offset - block->offset; + return block->start + padding + (block, forTrace ? o - vm::TargetBytesPerWord : o) + o; +} + + +Promise* offsetPromise(Context* con, bool forTrace) { + return new(con->zone) OffsetPromise(con, con->lastBlock, con->code.length(), forTrace); +} + + +OffsetListener::OffsetListener(vm::System* s, uint8_t* instruction): + s(s), + instruction(instruction) +{ } + +bool OffsetListener::resolve(int64_t value, void** location) { + void* p = updateOffset(s, instruction, value); + if (location) *location = p; + return false; +} + + +OffsetTask::OffsetTask(Task* next, Promise* promise, Promise* instructionOffset): + Task(next), + promise(promise), + instructionOffset(instructionOffset) +{ } + +void OffsetTask::run(Context* con) { + if (promise->resolved()) { + updateOffset + (con->s, con->result + instructionOffset->value(), promise->value()); + } else { + new (promise->listen(sizeof(OffsetListener))) + OffsetListener(con->s, con->result + instructionOffset->value()); + } +} + +void appendOffsetTask(Context* con, Promise* promise, Promise* instructionOffset) { + con->tasks = new(con->zone) OffsetTask(con->tasks, promise, instructionOffset); +} + +bool bounded(int right, int left, int32_t v) { + return ((v << left) >> left) == v and ((v >> right) << right) == v; +} + +void* updateOffset(vm::System* s, uint8_t* instruction, int64_t value) { + // ARM's PC is two words ahead, and branches drop the bottom 2 bits. + int32_t v = (reinterpret_cast(value) - (instruction + 8)) >> 2; + + int32_t mask; + expect(s, bounded(0, 8, v)); + mask = 0xFFFFFF; + + int32_t* p = reinterpret_cast(instruction); + *p = (v & mask) | ((~mask) & *p); + + return instruction + 4; +} + +ConstantPoolEntry::ConstantPoolEntry(Context* con, Promise* constant, ConstantPoolEntry* next, + Promise* callOffset): + con(con), constant(constant), next(next), callOffset(callOffset), + address(0) +{ } + +int64_t ConstantPoolEntry::value() { + assert(con, resolved()); + + return reinterpret_cast(address); +} + +bool ConstantPoolEntry::resolved() { + return address != 0; +} + +ConstantPoolListener::ConstantPoolListener(vm::System* s, vm::target_uintptr_t* address, + uint8_t* returnAddress): + s(s), + address(address), + returnAddress(returnAddress) +{ } + +bool ConstantPoolListener::resolve(int64_t value, void** location) { + *address = value; + if (location) { + *location = returnAddress ? static_cast(returnAddress) : address; + } + return true; +} + +PoolOffset::PoolOffset(MyBlock* block, ConstantPoolEntry* entry, unsigned offset): + block(block), entry(entry), next(0), offset(offset) +{ } + +PoolEvent::PoolEvent(PoolOffset* poolOffsetHead, PoolOffset* poolOffsetTail, + unsigned offset): + poolOffsetHead(poolOffsetHead), poolOffsetTail(poolOffsetTail), next(0), + offset(offset) +{ } + +void appendConstantPoolEntry(Context* con, Promise* constant, Promise* callOffset) { + if (constant->resolved()) { + // make a copy, since the original might be allocated on the + // stack, and we need our copy to live until assembly is complete + constant = new(con->zone) ResolvedPromise(constant->value()); + } + + con->constantPool = new(con->zone) ConstantPoolEntry(con, constant, con->constantPool, callOffset); + + ++ con->constantPoolCount; + + PoolOffset* o = new(con->zone) PoolOffset(con->lastBlock, con->constantPool, con->code.length() - con->lastBlock->offset); + + if (DebugPool) { + fprintf(stderr, "add pool offset %p %d to block %p\n", + o, o->offset, con->lastBlock); + } + + if (con->lastBlock->poolOffsetTail) { + con->lastBlock->poolOffsetTail->next = o; + } else { + con->lastBlock->poolOffsetHead = o; + } + con->lastBlock->poolOffsetTail = o; +} + +void appendPoolEvent(Context* con, MyBlock* b, unsigned offset, PoolOffset* head, + PoolOffset* tail) +{ + PoolEvent* e = new(con->zone) PoolEvent(head, tail, offset); + + if (b->poolEventTail) { + b->poolEventTail->next = e; + } else { + b->poolEventHead = e; + } + b->poolEventTail = e; +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/fixup.h b/src/codegen/target/arm/fixup.h new file mode 100644 index 0000000000..693c9c32b1 --- /dev/null +++ b/src/codegen/target/arm/fixup.h @@ -0,0 +1,140 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H + +#include "avian/target.h" + +#include +#include +#include "avian/alloc-vector.h" + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace arm { + +const bool DebugPool = false; + +const int32_t PoolOffsetMask = 0xFFF; + +class Task { + public: + Task(Task* next): next(next) { } + + virtual void run(Context* con) = 0; + + Task* next; +}; + +class OffsetPromise: public Promise { + public: + OffsetPromise(Context* con, MyBlock* block, unsigned offset, bool forTrace); + + virtual bool resolved(); + + virtual int64_t value(); + + Context* con; + MyBlock* block; + unsigned offset; + bool forTrace; +}; + +Promise* offsetPromise(Context* con, bool forTrace = false); + +class OffsetListener: public Promise::Listener { + public: + OffsetListener(vm::System* s, uint8_t* instruction); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + uint8_t* instruction; +}; + +class OffsetTask: public Task { + public: + OffsetTask(Task* next, Promise* promise, Promise* instructionOffset); + + virtual void run(Context* con); + + Promise* promise; + Promise* instructionOffset; +}; + +void appendOffsetTask(Context* con, Promise* promise, Promise* instructionOffset); + +void* updateOffset(vm::System* s, uint8_t* instruction, int64_t value); + +class ConstantPoolEntry: public Promise { + public: + ConstantPoolEntry(Context* con, Promise* constant, ConstantPoolEntry* next, + Promise* callOffset); + + virtual int64_t value(); + + virtual bool resolved(); + + Context* con; + Promise* constant; + ConstantPoolEntry* next; + Promise* callOffset; + void* address; + unsigned constantPoolCount; +}; + +class ConstantPoolListener: public Promise::Listener { + public: + ConstantPoolListener(vm::System* s, vm::target_uintptr_t* address, + uint8_t* returnAddress); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + vm::target_uintptr_t* address; + uint8_t* returnAddress; +}; + +class PoolOffset { + public: + PoolOffset(MyBlock* block, ConstantPoolEntry* entry, unsigned offset); + + MyBlock* block; + ConstantPoolEntry* entry; + PoolOffset* next; + unsigned offset; +}; + +class PoolEvent { + public: + PoolEvent(PoolOffset* poolOffsetHead, PoolOffset* poolOffsetTail, + unsigned offset); + + PoolOffset* poolOffsetHead; + PoolOffset* poolOffsetTail; + PoolEvent* next; + unsigned offset; +}; + +void appendConstantPoolEntry(Context* con, Promise* constant, Promise* callOffset); + +void appendPoolEvent(Context* con, MyBlock* b, unsigned offset, PoolOffset* head, + PoolOffset* tail); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_PROMISE_H diff --git a/src/codegen/target/arm/multimethod.cpp b/src/codegen/target/arm/multimethod.cpp new file mode 100644 index 0000000000..a88180fc9b --- /dev/null +++ b/src/codegen/target/arm/multimethod.cpp @@ -0,0 +1,141 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "operations.h" + +#include "multimethod.h" +#include "../multimethod.h" + +namespace avian { +namespace codegen { +namespace arm { + +using namespace util; + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2) +{ + return operation + + (lir::BinaryOperationCount * operand1) + + (lir::BinaryOperationCount * lir::OperandTypeCount * operand2); +} + +unsigned index(ArchitectureContext* con UNUSED, + lir::TernaryOperation operation, + lir::OperandType operand1) +{ + assert(con, not isBranch(operation)); + + return operation + (lir::NonBranchTernaryOperationCount * operand1); +} + +unsigned branchIndex(ArchitectureContext* con UNUSED, lir::OperandType operand1, + lir::OperandType operand2) +{ + return operand1 + (lir::OperandTypeCount * operand2); +} + +void populateTables(ArchitectureContext* con) { + const lir::OperandType C = lir::ConstantOperand; + const lir::OperandType A = lir::AddressOperand; + const lir::OperandType R = lir::RegisterOperand; + const lir::OperandType M = lir::MemoryOperand; + + OperationType* zo = con->operations; + UnaryOperationType* uo = con->unaryOperations; + BinaryOperationType* bo = con->binaryOperations; + TernaryOperationType* to = con->ternaryOperations; + BranchOperationType* bro = con->branchOperations; + + zo[lir::Return] = return_; + zo[lir::LoadBarrier] = memoryBarrier; + zo[lir::StoreStoreBarrier] = memoryBarrier; + zo[lir::StoreLoadBarrier] = memoryBarrier; + zo[lir::Trap] = trap; + + uo[Multimethod::index(lir::LongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::AlignedLongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::LongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::AlignedLongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::Jump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::Jump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::AlignedJump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::AlignedJump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::Call, C)] = CAST1(callC); + uo[Multimethod::index(lir::Call, R)] = CAST1(callR); + + uo[Multimethod::index(lir::AlignedCall, C)] = CAST1(callC); + uo[Multimethod::index(lir::AlignedCall, R)] = CAST1(callR); + + bo[index(con, lir::Move, R, R)] = CAST2(moveRR); + bo[index(con, lir::Move, C, R)] = CAST2(moveCR); + bo[index(con, lir::Move, C, M)] = CAST2(moveCM); + bo[index(con, lir::Move, M, R)] = CAST2(moveMR); + bo[index(con, lir::Move, R, M)] = CAST2(moveRM); + bo[index(con, lir::Move, A, R)] = CAST2(moveAR); + + bo[index(con, lir::MoveZ, R, R)] = CAST2(moveZRR); + bo[index(con, lir::MoveZ, M, R)] = CAST2(moveZMR); + bo[index(con, lir::MoveZ, C, R)] = CAST2(moveCR); + + bo[index(con, lir::Negate, R, R)] = CAST2(negateRR); + + bo[index(con, lir::FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); + bo[index(con, lir::FloatNegate, R, R)] = CAST2(floatNegateRR); + bo[index(con, lir::Float2Float, R, R)] = CAST2(float2FloatRR); + bo[index(con, lir::Float2Int, R, R)] = CAST2(float2IntRR); + bo[index(con, lir::Int2Float, R, R)] = CAST2(int2FloatRR); + bo[index(con, lir::FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); + + to[index(con, lir::Add, R)] = CAST3(addR); + + to[index(con, lir::Subtract, R)] = CAST3(subR); + + to[index(con, lir::Multiply, R)] = CAST3(multiplyR); + + to[index(con, lir::FloatAdd, R)] = CAST3(floatAddR); + to[index(con, lir::FloatSubtract, R)] = CAST3(floatSubtractR); + to[index(con, lir::FloatMultiply, R)] = CAST3(floatMultiplyR); + to[index(con, lir::FloatDivide, R)] = CAST3(floatDivideR); + + to[index(con, lir::ShiftLeft, R)] = CAST3(shiftLeftR); + to[index(con, lir::ShiftLeft, C)] = CAST3(shiftLeftC); + + to[index(con, lir::ShiftRight, R)] = CAST3(shiftRightR); + to[index(con, lir::ShiftRight, C)] = CAST3(shiftRightC); + + to[index(con, lir::UnsignedShiftRight, R)] = CAST3(unsignedShiftRightR); + to[index(con, lir::UnsignedShiftRight, C)] = CAST3(unsignedShiftRightC); + + to[index(con, lir::And, R)] = CAST3(andR); + to[index(con, lir::And, C)] = CAST3(andC); + + to[index(con, lir::Or, R)] = CAST3(orR); + + to[index(con, lir::Xor, R)] = CAST3(xorR); + + bro[branchIndex(con, R, R)] = CAST_BRANCH(branchRR); + bro[branchIndex(con, C, R)] = CAST_BRANCH(branchCR); + bro[branchIndex(con, C, M)] = CAST_BRANCH(branchCM); + bro[branchIndex(con, R, M)] = CAST_BRANCH(branchRM); +} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/multimethod.h b/src/codegen/target/arm/multimethod.h new file mode 100644 index 0000000000..cda1daff37 --- /dev/null +++ b/src/codegen/target/arm/multimethod.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H + +#include +#include + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST3(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +namespace avian { +namespace codegen { +namespace arm { + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2); + +unsigned index(ArchitectureContext* con UNUSED, + lir::TernaryOperation operation, + lir::OperandType operand1); + +unsigned branchIndex(ArchitectureContext* con UNUSED, lir::OperandType operand1, + lir::OperandType operand2); + +void populateTables(ArchitectureContext* con); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_MULTIMETHOD_H diff --git a/src/codegen/target/arm/operations.cpp b/src/codegen/target/arm/operations.cpp new file mode 100644 index 0000000000..b896a88f00 --- /dev/null +++ b/src/codegen/target/arm/operations.cpp @@ -0,0 +1,1235 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "operations.h" +#include "encode.h" +#include "block.h" +#include "fixup.h" +#include "multimethod.h" + +namespace avian { +namespace codegen { +namespace arm { + +using namespace isa; +using namespace avian::util; + +inline bool isOfWidth(int64_t i, int size) { return static_cast(i) >> size == 0; } + +inline unsigned lo8(int64_t i) { return (unsigned)(i&MASK_LO8); } + +void andC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void shiftLeftR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) +{ + if (size == 8) { + int tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); + ResolvedPromise maskPromise(0x3F); + lir::Constant mask(&maskPromise); + lir::Register dst(tmp3); + andC(con, 4, &mask, a, &dst); + emit(con, lsl(tmp1, b->high, tmp3)); + emit(con, rsbi(tmp2, tmp3, 32)); + emit(con, orrsh(tmp1, tmp1, b->low, tmp2, LSR)); + emit(con, SETS(subi(t->high, tmp3, 32))); + emit(con, SETCOND(mov(t->high, tmp1), MI)); + emit(con, SETCOND(lsl(t->high, b->low, t->high), PL)); + emit(con, lsl(t->low, b->low, tmp3)); + freeTemp(con, tmp1); freeTemp(con, tmp2); freeTemp(con, tmp3); + } else { + int tmp = newTemp(con); + ResolvedPromise maskPromise(0x1F); + lir::Constant mask(&maskPromise); + lir::Register dst(tmp); + andC(con, size, &mask, a, &dst); + emit(con, lsl(t->low, b->low, tmp)); + freeTemp(con, tmp); + } +} + +void moveRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void shiftLeftC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t) +{ + assert(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, lsli(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +void shiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) +{ + if (size == 8) { + int tmp1 = newTemp(con), tmp2 = newTemp(con), tmp3 = newTemp(con); + ResolvedPromise maskPromise(0x3F); + lir::Constant mask(&maskPromise); + lir::Register dst(tmp3); + andC(con, 4, &mask, a, &dst); + emit(con, lsr(tmp1, b->low, tmp3)); + emit(con, rsbi(tmp2, tmp3, 32)); + emit(con, orrsh(tmp1, tmp1, b->high, tmp2, LSL)); + emit(con, SETS(subi(t->low, tmp3, 32))); + emit(con, SETCOND(mov(t->low, tmp1), MI)); + emit(con, SETCOND(asr(t->low, b->high, t->low), PL)); + emit(con, asr(t->high, b->high, tmp3)); + freeTemp(con, tmp1); freeTemp(con, tmp2); freeTemp(con, tmp3); + } else { + int tmp = newTemp(con); + ResolvedPromise maskPromise(0x1F); + lir::Constant mask(&maskPromise); + lir::Register dst(tmp); + andC(con, size, &mask, a, &dst); + emit(con, asr(t->low, b->low, tmp)); + freeTemp(con, tmp); + } +} + +void shiftRightC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t) +{ + assert(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, asri(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +void unsignedShiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) +{ + int tmpShift = newTemp(con); + ResolvedPromise maskPromise(size == 8 ? 0x3F : 0x1F); + lir::Constant mask(&maskPromise); + lir::Register dst(tmpShift); + andC(con, 4, &mask, a, &dst); + emit(con, lsr(t->low, b->low, tmpShift)); + if (size == 8) { + int tmpHi = newTemp(con), tmpLo = newTemp(con); + emit(con, SETS(rsbi(tmpHi, tmpShift, 32))); + emit(con, lsl(tmpLo, b->high, tmpHi)); + emit(con, orr(t->low, t->low, tmpLo)); + emit(con, addi(tmpHi, tmpShift, -32)); + emit(con, lsr(tmpLo, b->high, tmpHi)); + emit(con, orr(t->low, t->low, tmpLo)); + emit(con, lsr(t->high, b->high, tmpShift)); + freeTemp(con, tmpHi); freeTemp(con, tmpLo); + } + freeTemp(con, tmpShift); +} + +void unsignedShiftRightC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t) +{ + assert(con, size == vm::TargetBytesPerWord); + if (getValue(a) & 0x1F) { + emit(con, lsri(t->low, b->low, getValue(a) & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } +} + +bool +needJump(MyBlock* b) +{ + return b->next or b->size != (b->size & PoolOffsetMask); +} + +unsigned +padding(MyBlock* b, unsigned offset) +{ + unsigned total = 0; + for (PoolEvent* e = b->poolEventHead; e; e = e->next) { + if (e->offset <= offset) { + if (needJump(b)) { + total += vm::TargetBytesPerWord; + } + for (PoolOffset* o = e->poolOffsetHead; o; o = o->next) { + total += vm::TargetBytesPerWord; + } + } else { + break; + } + } + return total; +} + +void resolve(MyBlock* b) +{ + Context* con = b->context; + + if (b->poolOffsetHead) { + if (con->poolOffsetTail) { + con->poolOffsetTail->next = b->poolOffsetHead; + } else { + con->poolOffsetHead = b->poolOffsetHead; + } + con->poolOffsetTail = b->poolOffsetTail; + } + + if (con->poolOffsetHead) { + bool append; + if (b->next == 0 or b->next->poolEventHead) { + append = true; + } else { + int32_t v = (b->start + b->size + b->next->size + vm::TargetBytesPerWord - 8) + - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); + + append = (v != (v & PoolOffsetMask)); + + if (DebugPool) { + fprintf(stderr, + "current %p %d %d next %p %d %d\n", + b, b->start, b->size, b->next, b->start + b->size, + b->next->size); + fprintf(stderr, + "offset %p %d is of distance %d to next block; append? %d\n", + con->poolOffsetHead, con->poolOffsetHead->offset, v, append); + } + } + + if (append) { +#ifndef NDEBUG + int32_t v = (b->start + b->size - 8) + - (con->poolOffsetHead->offset + con->poolOffsetHead->block->start); + + expect(con, v == (v & PoolOffsetMask)); +#endif // not NDEBUG + + appendPoolEvent(con, b, b->size, con->poolOffsetHead, con->poolOffsetTail); + + if (DebugPool) { + for (PoolOffset* o = con->poolOffsetHead; o; o = o->next) { + fprintf(stderr, + "include %p %d in pool event %p at offset %d in block %p\n", + o, o->offset, b->poolEventTail, b->size, b); + } + } + + con->poolOffsetHead = 0; + con->poolOffsetTail = 0; + } + } +} + +void jumpR(Context* con, unsigned size UNUSED, lir::Register* target) +{ + assert(con, size == vm::TargetBytesPerWord); + emit(con, bx(target->low)); +} + +void swapRR(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + assert(con, aSize == vm::TargetBytesPerWord); + assert(con, bSize == vm::TargetBytesPerWord); + + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveRR(con, aSize, a, bSize, &tmp); + moveRR(con, bSize, b, aSize, a); + moveRR(con, bSize, &tmp, bSize, b); + con->client->releaseTemporary(tmp.low); +} + +void moveRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst) +{ + bool srcIsFpr = isFpr(src); + bool dstIsFpr = isFpr(dst); + if (srcIsFpr || dstIsFpr) { // FPR(s) involved + assert(con, srcSize == dstSize); + const bool dprec = srcSize == 8; + if (srcIsFpr && dstIsFpr) { // FPR to FPR + if (dprec) emit(con, fcpyd(fpr64(dst), fpr64(src))); // double + else emit(con, fcpys(fpr32(dst), fpr32(src))); // single + } else if (srcIsFpr) { // FPR to GPR + if (dprec) emit(con, fmrrd(dst->low, dst->high, fpr64(src))); + else emit(con, fmrs(dst->low, fpr32(src))); + } else { // GPR to FPR + if (dprec) emit(con, fmdrr(fpr64(dst->low), src->low, src->high)); + else emit(con, fmsr(fpr32(dst), src->low)); + } + return; + } + + switch (srcSize) { + case 1: + emit(con, lsli(dst->low, src->low, 24)); + emit(con, asri(dst->low, dst->low, 24)); + break; + + case 2: + emit(con, lsli(dst->low, src->low, 16)); + emit(con, asri(dst->low, dst->low, 16)); + break; + + case 4: + case 8: + if (srcSize == 4 and dstSize == 8) { + moveRR(con, 4, src, 4, dst); + emit(con, asri(dst->high, src->low, 31)); + } else if (srcSize == 8 and dstSize == 8) { + lir::Register srcHigh(src->high); + lir::Register dstHigh(dst->high); + + if (src->high == dst->low) { + if (src->low == dst->high) { + swapRR(con, 4, src, 4, dst); + } else { + moveRR(con, 4, &srcHigh, 4, &dstHigh); + moveRR(con, 4, src, 4, dst); + } + } else { + moveRR(con, 4, src, 4, dst); + moveRR(con, 4, &srcHigh, 4, &dstHigh); + } + } else if (src->low != dst->low) { + emit(con, mov(dst->low, src->low)); + } + break; + + default: abort(con); + } +} + +void moveZRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned, lir::Register* dst) +{ + switch (srcSize) { + case 2: + emit(con, lsli(dst->low, src->low, 16)); + emit(con, lsri(dst->low, dst->low, 16)); + break; + + default: abort(con); + } +} + +void moveCR(Context* con, unsigned size, lir::Constant* src, + unsigned, lir::Register* dst); + +void moveCR2(Context* con, unsigned size, lir::Constant* src, + lir::Register* dst, Promise* callOffset) +{ + if (isFpr(dst)) { // floating-point + lir::Register tmp = size > 4 ? makeTemp64(con) : + makeTemp(con); + moveCR(con, size, src, size, &tmp); + moveRR(con, size, &tmp, size, dst); + freeTemp(con, tmp); + } else if (size > 4) { + uint64_t value = (uint64_t)src->value->value(); + ResolvedPromise loBits(value & MASK_LO32); + lir::Constant srcLo(&loBits); + ResolvedPromise hiBits(value >> 32); + lir::Constant srcHi(&hiBits); + lir::Register dstHi(dst->high); + moveCR(con, 4, &srcLo, 4, dst); + moveCR(con, 4, &srcHi, 4, &dstHi); + } else if (src->value->resolved() and isOfWidth(getValue(src), 8)) { + emit(con, movi(dst->low, lo8(getValue(src)))); // fits in immediate + } else { + appendConstantPoolEntry(con, src->value, callOffset); + emit(con, ldri(dst->low, ProgramCounter, 0)); // load 32 bits + } +} + +void moveCR(Context* con, unsigned size, lir::Constant* src, + unsigned, lir::Register* dst) +{ + moveCR2(con, size, src, dst, 0); +} + +void addR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, SETS(add(t->low, a->low, b->low))); + emit(con, adc(t->high, a->high, b->high)); + } else { + emit(con, add(t->low, a->low, b->low)); + } +} + +void subR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, SETS(rsb(t->low, a->low, b->low))); + emit(con, rsc(t->high, a->high, b->high)); + } else { + emit(con, rsb(t->low, a->low, b->low)); + } +} + +void addC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) +{ + assert(con, size == vm::TargetBytesPerWord); + + int32_t v = a->value->value(); + if (v) { + if (v > 0 and v < 256) { + emit(con, addi(dst->low, b->low, v)); + } else if (v > 0 and v < 1024 and v % 4 == 0) { + emit(con, addi(dst->low, b->low, v >> 2, 15)); + } else { + // todo + abort(con); + } + } else { + moveRR(con, size, b, size, dst); + } +} + +void subC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) +{ + assert(con, size == vm::TargetBytesPerWord); + + int32_t v = a->value->value(); + if (v) { + if (v > 0 and v < 256) { + emit(con, subi(dst->low, b->low, v)); + } else if (v > 0 and v < 1024 and v % 4 == 0) { + emit(con, subi(dst->low, b->low, v >> 2, 15)); + } else { + // todo + abort(con); + } + } else { + moveRR(con, size, b, size, dst); + } +} + +void multiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + bool useTemporaries = b->low == t->low; + int tmpLow = useTemporaries ? con->client->acquireTemporary(GPR_MASK) : t->low; + int tmpHigh = useTemporaries ? con->client->acquireTemporary(GPR_MASK) : t->high; + + emit(con, umull(tmpLow, tmpHigh, a->low, b->low)); + emit(con, mla(tmpHigh, a->low, b->high, tmpHigh)); + emit(con, mla(tmpHigh, a->high, b->low, tmpHigh)); + + if (useTemporaries) { + emit(con, mov(t->low, tmpLow)); + emit(con, mov(t->high, tmpHigh)); + con->client->releaseTemporary(tmpLow); + con->client->releaseTemporary(tmpHigh); + } + } else { + emit(con, mul(t->low, a->low, b->low)); + } +} + +void floatAbsoluteRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b) { + if (size == 8) { + emit(con, fabsd(fpr64(b), fpr64(a))); + } else { + emit(con, fabss(fpr32(b), fpr32(a))); + } +} + +void floatNegateRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b) { + if (size == 8) { + emit(con, fnegd(fpr64(b), fpr64(a))); + } else { + emit(con, fnegs(fpr32(b), fpr32(a))); + } +} + +void float2FloatRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b) { + if (size == 8) { + emit(con, fcvtsd(fpr32(b), fpr64(a))); + } else { + emit(con, fcvtds(fpr64(b), fpr32(a))); + } +} + +void float2IntRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b) { + int tmp = newTemp(con, FPR_MASK); + int ftmp = fpr32(tmp); + if (size == 8) { // double to int + emit(con, ftosizd(ftmp, fpr64(a))); + } else { // float to int + emit(con, ftosizs(ftmp, fpr32(a))); + } // else thunked + emit(con, fmrs(b->low, ftmp)); + freeTemp(con, tmp); +} + +void int2FloatRR(Context* con, unsigned, lir::Register* a, unsigned size, lir::Register* b) { + emit(con, fmsr(fpr32(b), a->low)); + if (size == 8) { // int to double + emit(con, fsitod(fpr64(b), fpr32(b))); + } else { // int to float + emit(con, fsitos(fpr32(b), fpr32(b))); + } // else thunked +} + +void floatSqrtRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b) { + if (size == 8) { + emit(con, fsqrtd(fpr64(b), fpr64(a))); + } else { + emit(con, fsqrts(fpr32(b), fpr32(a))); + } +} + +void floatAddR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, faddd(fpr64(t), fpr64(a), fpr64(b))); + } else { + emit(con, fadds(fpr32(t), fpr32(a), fpr32(b))); + } +} + +void floatSubtractR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, fsubd(fpr64(t), fpr64(b), fpr64(a))); + } else { + emit(con, fsubs(fpr32(t), fpr32(b), fpr32(a))); + } +} + +void floatMultiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, fmuld(fpr64(t), fpr64(a), fpr64(b))); + } else { + emit(con, fmuls(fpr32(t), fpr32(a), fpr32(b))); + } +} + +void floatDivideR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if (size == 8) { + emit(con, fdivd(fpr64(t), fpr64(b), fpr64(a))); + } else { + emit(con, fdivs(fpr32(t), fpr32(b), fpr32(a))); + } +} + +int normalize(Context* con, int offset, int index, unsigned scale, + bool* preserveIndex, bool* release) +{ + if (offset != 0 or scale != 1) { + lir::Register normalizedIndex + (*preserveIndex ? con->client->acquireTemporary(GPR_MASK) : index); + + if (*preserveIndex) { + *release = true; + *preserveIndex = false; + } else { + *release = false; + } + + int scaled; + + if (scale != 1) { + lir::Register unscaledIndex(index); + + ResolvedPromise scalePromise(log(scale)); + lir::Constant scaleConstant(&scalePromise); + + shiftLeftC(con, vm::TargetBytesPerWord, &scaleConstant, + &unscaledIndex, &normalizedIndex); + + scaled = normalizedIndex.low; + } else { + scaled = index; + } + + if (offset != 0) { + lir::Register untranslatedIndex(scaled); + + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, vm::TargetBytesPerWord, &offsetConstant, vm::TargetBytesPerWord, &tmp); + addR(con, vm::TargetBytesPerWord, &tmp, &untranslatedIndex, &normalizedIndex); + con->client->releaseTemporary(tmp.low); + } + + return normalizedIndex.low; + } else { + *release = false; + return index; + } +} + +void store(Context* con, unsigned size, lir::Register* src, + int base, int offset, int index, unsigned scale, bool preserveIndex) +{ + if (index != lir::NoRegister) { + bool release; + int normalized = normalize + (con, offset, index, scale, &preserveIndex, &release); + + if (!isFpr(src)) { // GPR store + switch (size) { + case 1: + emit(con, strb(src->low, base, normalized)); + break; + + case 2: + emit(con, strh(src->low, base, normalized)); + break; + + case 4: + emit(con, str(src->low, base, normalized)); + break; + + case 8: { // split into 2 32-bit stores + lir::Register srcHigh(src->high); + store(con, 4, &srcHigh, base, 0, normalized, 1, preserveIndex); + store(con, 4, src, base, 4, normalized, 1, preserveIndex); + } break; + + default: abort(con); + } + } else { // FPR store + lir::Register base_(base), + normalized_(normalized), + absAddr = makeTemp(con); + // FPR stores have only bases, so we must add the index + addR(con, vm::TargetBytesPerWord, &base_, &normalized_, &absAddr); + // double-precision + if (size == 8) emit(con, fstd(fpr64(src), absAddr.low)); + // single-precision + else emit(con, fsts(fpr32(src), absAddr.low)); + freeTemp(con, absAddr); + } + + if (release) con->client->releaseTemporary(normalized); + } else if (size == 8 + or abs(offset) == (abs(offset) & 0xFF) + or (size != 2 and abs(offset) == (abs(offset) & 0xFFF))) + { + if (!isFpr(src)) { // GPR store + switch (size) { + case 1: + emit(con, strbi(src->low, base, offset)); + break; + + case 2: + emit(con, strhi(src->low, base, offset)); + break; + + case 4: + emit(con, stri(src->low, base, offset)); + break; + + case 8: { // split into 2 32-bit stores + lir::Register srcHigh(src->high); + store(con, 4, &srcHigh, base, offset, lir::NoRegister, 1, false); + store(con, 4, src, base, offset + 4, lir::NoRegister, 1, false); + } break; + + default: abort(con); + } + } else { // FPR store + // double-precision + if (size == 8) emit(con, fstd(fpr64(src), base, offset)); + // single-precision + else emit(con, fsts(fpr32(src), base, offset)); + } + } else { + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(con, vm::TargetBytesPerWord, &offsetConstant, + vm::TargetBytesPerWord, &tmp); + + store(con, size, src, base, 0, tmp.low, 1, false); + + con->client->releaseTemporary(tmp.low); + } +} + +void moveRM(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst) +{ + assert(con, srcSize == dstSize); + + store(con, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); +} + +void load(Context* con, unsigned srcSize, int base, int offset, int index, + unsigned scale, unsigned dstSize, lir::Register* dst, + bool preserveIndex, bool signExtend) +{ + if (index != lir::NoRegister) { + bool release; + int normalized = normalize + (con, offset, index, scale, &preserveIndex, &release); + + if (!isFpr(dst)) { // GPR load + switch (srcSize) { + case 1: + if (signExtend) { + emit(con, ldrsb(dst->low, base, normalized)); + } else { + emit(con, ldrb(dst->low, base, normalized)); + } + break; + + case 2: + if (signExtend) { + emit(con, ldrsh(dst->low, base, normalized)); + } else { + emit(con, ldrh(dst->low, base, normalized)); + } + break; + + case 4: + case 8: { + if (srcSize == 4 and dstSize == 8) { + load(con, 4, base, 0, normalized, 1, 4, dst, preserveIndex, + false); + moveRR(con, 4, dst, 8, dst); + } else if (srcSize == 8 and dstSize == 8) { + lir::Register dstHigh(dst->high); + load(con, 4, base, 0, normalized, 1, 4, &dstHigh, + preserveIndex, false); + load(con, 4, base, 4, normalized, 1, 4, dst, preserveIndex, + false); + } else { + emit(con, ldr(dst->low, base, normalized)); + } + } break; + + default: abort(con); + } + } else { // FPR load + lir::Register base_(base), + normalized_(normalized), + absAddr = makeTemp(con); + // VFP loads only have bases, so we must add the index + addR(con, vm::TargetBytesPerWord, &base_, &normalized_, &absAddr); + // double-precision + if (srcSize == 8) emit(con, fldd(fpr64(dst), absAddr.low)); + // single-precision + else emit(con, flds(fpr32(dst), absAddr.low)); + freeTemp(con, absAddr); + } + + if (release) con->client->releaseTemporary(normalized); + } else if ((srcSize == 8 and dstSize == 8) + or abs(offset) == (abs(offset) & 0xFF) + or (srcSize != 2 + and (srcSize != 1 or not signExtend) + and abs(offset) == (abs(offset) & 0xFFF))) + { + if (!isFpr(dst)) { // GPR load + switch (srcSize) { + case 1: + if (signExtend) { + emit(con, ldrsbi(dst->low, base, offset)); + } else { + emit(con, ldrbi(dst->low, base, offset)); + } + break; + + case 2: + if (signExtend) { + emit(con, ldrshi(dst->low, base, offset)); + } else { + emit(con, ldrhi(dst->low, base, offset)); + } + break; + + case 4: + emit(con, ldri(dst->low, base, offset)); + break; + + case 8: { + if (dstSize == 8) { + lir::Register dstHigh(dst->high); + load(con, 4, base, offset, lir::NoRegister, 1, 4, &dstHigh, false, + false); + load(con, 4, base, offset + 4, lir::NoRegister, 1, 4, dst, false, + false); + } else { + emit(con, ldri(dst->low, base, offset)); + } + } break; + + default: abort(con); + } + } else { // FPR load + // double-precision + if (srcSize == 8) emit(con, fldd(fpr64(dst), base, offset)); + // single-precision + else emit(con, flds(fpr32(dst), base, offset)); + } + } else { + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + moveCR(con, vm::TargetBytesPerWord, &offsetConstant, vm::TargetBytesPerWord, + &tmp); + + load(con, srcSize, base, 0, tmp.low, 1, dstSize, dst, false, + signExtend); + + con->client->releaseTemporary(tmp.low); + } +} + +void moveMR(Context* con, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst) +{ + load(con, srcSize, src->base, src->offset, src->index, src->scale, + dstSize, dst, true, true); +} + +void moveZMR(Context* con, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst) +{ + load(con, srcSize, src->base, src->offset, src->index, src->scale, + dstSize, dst, true, false); +} + +void andR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) +{ + if (size == 8) emit(con, and_(dst->high, a->high, b->high)); + emit(con, and_(dst->low, a->low, b->low)); +} + +void andC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) +{ + int64_t v = a->value->value(); + + if (size == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + lir::Register dh(dst->high); + + andC(con, 4, &al, b, dst); + andC(con, 4, &ah, &bh, &dh); + } else { + uint32_t v32 = static_cast(v); + if (v32 != 0xFFFFFFFF) { + if ((v32 & 0xFFFFFF00) == 0xFFFFFF00) { + emit(con, bici(dst->low, b->low, (~(v32 & 0xFF)) & 0xFF)); + } else if ((v32 & 0xFFFFFF00) == 0) { + emit(con, andi(dst->low, b->low, v32 & 0xFF)); + } else { + // todo: there are other cases we can handle in one + // instruction + + bool useTemporary = b->low == dst->low; + lir::Register tmp(dst->low); + if (useTemporary) { + tmp.low = con->client->acquireTemporary(GPR_MASK); + } + + moveCR(con, 4, a, 4, &tmp); + andR(con, 4, b, &tmp, dst); + + if (useTemporary) { + con->client->releaseTemporary(tmp.low); + } + } + } else { + moveRR(con, size, b, size, dst); + } + } +} + +void orR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) +{ + if (size == 8) emit(con, orr(dst->high, a->high, b->high)); + emit(con, orr(dst->low, a->low, b->low)); +} + +void xorR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) +{ + if (size == 8) emit(con, eor(dst->high, a->high, b->high)); + emit(con, eor(dst->low, a->low, b->low)); +} + +void moveAR2(Context* con, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst) +{ + assert(con, srcSize == 4 and dstSize == 4); + + lir::Constant constant(src->address); + moveCR(con, srcSize, &constant, dstSize, dst); + + lir::Memory memory(dst->low, 0, -1, 0); + moveMR(con, dstSize, &memory, dstSize, dst); +} + +void moveAR(Context* con, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst) +{ + moveAR2(con, srcSize, src, dstSize, dst); +} + +void compareRR(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(con, !(isFpr(a) ^ isFpr(b))); // regs must be of the same type + + if (!isFpr(a)) { // GPR compare + assert(con, aSize == 4 && bSize == 4); + /**///assert(con, b->low != a->low); + emit(con, cmp(b->low, a->low)); + } else { // FPR compare + assert(con, aSize == bSize); + if (aSize == 8) emit(con, fcmpd(fpr64(b), fpr64(a))); // double + else emit(con, fcmps(fpr32(b), fpr32(a))); // single + emit(con, fmstat()); + } +} + +void compareCR(Context* con, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(con, aSize == 4 and bSize == 4); + + if (!isFpr(b) && a->value->resolved() && + isOfWidth(a->value->value(), 8)) { + emit(con, cmpi(b->low, a->value->value())); + } else { + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, aSize, a, bSize, &tmp); + compareRR(con, bSize, &tmp, bSize, b); + con->client->releaseTemporary(tmp.low); + } +} + +void compareCM(Context* con, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b) +{ + assert(con, aSize == 4 and bSize == 4); + + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveMR(con, bSize, b, bSize, &tmp); + compareCR(con, aSize, a, bSize, &tmp); + con->client->releaseTemporary(tmp.low); +} + +void compareRM(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Memory* b) +{ + assert(con, aSize == 4 and bSize == 4); + + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveMR(con, bSize, b, bSize, &tmp); + compareRR(con, aSize, a, bSize, &tmp); + con->client->releaseTemporary(tmp.low); +} + +int32_t +branch(Context* con, lir::TernaryOperation op) +{ + switch (op) { + case lir::JumpIfEqual: + case lir::JumpIfFloatEqual: + return beq(0); + + case lir::JumpIfNotEqual: + case lir::JumpIfFloatNotEqual: + return bne(0); + + case lir::JumpIfLess: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatLessOrUnordered: + return blt(0); + + case lir::JumpIfGreater: + case lir::JumpIfFloatGreater: + return bgt(0); + + case lir::JumpIfLessOrEqual: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatLessOrEqualOrUnordered: + return ble(0); + + case lir::JumpIfGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + return bge(0); + + case lir::JumpIfFloatGreaterOrUnordered: + return bhi(0); + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + return bpl(0); + + default: + abort(con); + } +} + +void conditional(Context* con, int32_t branch, lir::Constant* target) +{ + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, branch); +} + +void branch(Context* con, lir::TernaryOperation op, lir::Constant* target) +{ + conditional(con, branch(con, op), target); +} + +void branchLong(Context* con, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned) +{ + compareSigned(con, 4, ah, 4, bh); + + unsigned next = 0; + + switch (op) { + case lir::JumpIfEqual: + case lir::JumpIfFloatEqual: + next = con->code.length(); + emit(con, bne(0)); + + compareSigned(con, 4, al, 4, bl); + conditional(con, beq(0), target); + break; + + case lir::JumpIfNotEqual: + case lir::JumpIfFloatNotEqual: + conditional(con, bne(0), target); + + compareSigned(con, 4, al, 4, bl); + conditional(con, bne(0), target); + break; + + case lir::JumpIfLess: + case lir::JumpIfFloatLess: + conditional(con, blt(0), target); + + next = con->code.length(); + emit(con, bgt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, blo(0), target); + break; + + case lir::JumpIfGreater: + case lir::JumpIfFloatGreater: + conditional(con, bgt(0), target); + + next = con->code.length(); + emit(con, blt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bhi(0), target); + break; + + case lir::JumpIfLessOrEqual: + case lir::JumpIfFloatLessOrEqual: + conditional(con, blt(0), target); + + next = con->code.length(); + emit(con, bgt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bls(0), target); + break; + + case lir::JumpIfGreaterOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + conditional(con, bgt(0), target); + + next = con->code.length(); + emit(con, blt(0)); + + compareUnsigned(con, 4, al, 4, bl); + conditional(con, bhs(0), target); + break; + + default: + abort(con); + } + + if (next) { + updateOffset + (con->s, con->code.data + next, reinterpret_cast + (con->code.data + con->code.length())); + } +} + +void branchRR(Context* con, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target) +{ + if (!isFpr(a) && size > vm::TargetBytesPerWord) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + branchLong(con, op, a, &ah, b, &bh, target, CAST2(compareRR), + CAST2(compareRR)); + } else { + compareRR(con, size, a, size, b); + branch(con, op, target); + } +} + +void branchCR(Context* con, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target) +{ + assert(con, !isFloatBranch(op)); + + if (size > vm::TargetBytesPerWord) { + int64_t v = a->value->value(); + + ResolvedPromise low(v & ~static_cast(0)); + lir::Constant al(&low); + + ResolvedPromise high((v >> 32) & ~static_cast(0)); + lir::Constant ah(&high); + + lir::Register bh(b->high); + + branchLong(con, op, &al, &ah, b, &bh, target, CAST2(compareCR), + CAST2(compareCR)); + } else { + compareCR(con, size, a, size, b); + branch(con, op, target); + } +} + +void branchRM(Context* con, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target) +{ + assert(con, !isFloatBranch(op)); + assert(con, size <= vm::TargetBytesPerWord); + + compareRM(con, size, a, size, b); + branch(con, op, target); +} + +void branchCM(Context* con, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target) +{ + assert(con, !isFloatBranch(op)); + assert(con, size <= vm::TargetBytesPerWord); + + compareCM(con, size, a, size, b); + branch(con, op, target); +} + +ShiftMaskPromise* +shiftMaskPromise(Context* con, Promise* base, unsigned shift, int64_t mask) +{ + return new(con->zone) ShiftMaskPromise(base, shift, mask); +} + +void moveCM(Context* con, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Memory* dst) +{ + switch (dstSize) { + case 8: { + lir::Constant srcHigh + (shiftMaskPromise(con, src->value, 32, 0xFFFFFFFF)); + lir::Constant srcLow + (shiftMaskPromise(con, src->value, 0, 0xFFFFFFFF)); + + lir::Memory dstLow + (dst->base, dst->offset + 4, dst->index, dst->scale); + + moveCM(con, 4, &srcLow, 4, &dstLow); + moveCM(con, 4, &srcHigh, 4, dst); + } break; + + default: + lir::Register tmp(con->client->acquireTemporary(GPR_MASK)); + moveCR(con, srcSize, src, dstSize, &tmp); + moveRM(con, dstSize, &tmp, dstSize, dst); + con->client->releaseTemporary(tmp.low); + } +} + +void negateRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Register* dst) +{ + assert(con, srcSize == dstSize); + + emit(con, mvn(dst->low, src->low)); + emit(con, SETS(addi(dst->low, dst->low, 1))); + if (srcSize == 8) { + emit(con, mvn(dst->high, src->high)); + emit(con, adci(dst->high, dst->high, 0)); + } +} + +void callR(Context* con, unsigned size UNUSED, lir::Register* target) +{ + assert(con, size == vm::TargetBytesPerWord); + emit(con, blx(target->low)); +} + +void callC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assert(con, size == vm::TargetBytesPerWord); + + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, bl(0)); +} + +void longCallC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assert(con, size == vm::TargetBytesPerWord); + + lir::Register tmp(4); + moveCR2(con, vm::TargetBytesPerWord, target, &tmp, offsetPromise(con)); + callR(con, vm::TargetBytesPerWord, &tmp); +} + +void longJumpC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assert(con, size == vm::TargetBytesPerWord); + + lir::Register tmp(4); // a non-arg reg that we don't mind clobbering + moveCR2(con, vm::TargetBytesPerWord, target, &tmp, offsetPromise(con)); + jumpR(con, vm::TargetBytesPerWord, &tmp); +} + +void jumpC(Context* con, unsigned size UNUSED, lir::Constant* target) +{ + assert(con, size == vm::TargetBytesPerWord); + + appendOffsetTask(con, target->value, offsetPromise(con)); + emit(con, b(0)); +} + +void return_(Context* con) +{ + emit(con, bx(LinkRegister)); +} + +void trap(Context* con) +{ + emit(con, bkpt(0)); +} + +void memoryBarrier(Context*) {} + +} // namespace arm +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/arm/operations.h b/src/codegen/target/arm/operations.h new file mode 100644 index 0000000000..2d598b6d9e --- /dev/null +++ b/src/codegen/target/arm/operations.h @@ -0,0 +1,240 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H + +#include "registers.h" + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace arm { + +class Context; + +// shortcut functions + +inline int newTemp(Context* con) { + return con->client->acquireTemporary(GPR_MASK); +} + +inline int newTemp(Context* con, unsigned mask) { + return con->client->acquireTemporary(mask); +} + +inline void freeTemp(Context* con, int r) { + con->client->releaseTemporary(r); +} + +inline int64_t getValue(lir::Constant* con) { + return con->value->value(); +} + +inline lir::Register makeTemp(Context* con) { + lir::Register tmp(newTemp(con)); + return tmp; +} + +inline lir::Register makeTemp64(Context* con) { + lir::Register tmp(newTemp(con), newTemp(con)); + return tmp; +} + +inline void freeTemp(Context* con, const lir::Register& tmp) { + if (tmp.low != lir::NoRegister) freeTemp(con, tmp.low); + if (tmp.high != lir::NoRegister) freeTemp(con, tmp.high); +} + +void shiftLeftR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void moveRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void shiftLeftC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t); + +void shiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void shiftRightC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t); + +void unsignedShiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void unsignedShiftRightC(Context* con, unsigned size UNUSED, lir::Constant* a, lir::Register* b, lir::Register* t); + +bool needJump(MyBlock* b); + +unsigned padding(MyBlock* b, unsigned offset); + +void resolve(MyBlock* b); + +void jumpR(Context* con, unsigned size UNUSED, lir::Register* target); + +void swapRR(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void moveRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void moveZRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned, lir::Register* dst); + +void moveCR(Context* con, unsigned size, lir::Constant* src, + unsigned, lir::Register* dst); + +void moveCR2(Context* con, unsigned size, lir::Constant* src, + lir::Register* dst, Promise* callOffset); + +void moveCR(Context* con, unsigned size, lir::Constant* src, + unsigned, lir::Register* dst); + +void addR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void subR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void addC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void subC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void multiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void floatAbsoluteRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b); + +void floatNegateRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b); + +void float2FloatRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b); + +void float2IntRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b); + +void int2FloatRR(Context* con, unsigned, lir::Register* a, unsigned size, lir::Register* b); + +void floatSqrtRR(Context* con, unsigned size, lir::Register* a, unsigned, lir::Register* b); + +void floatAddR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void floatSubtractR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void floatMultiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void floatDivideR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +int normalize(Context* con, int offset, int index, unsigned scale, + bool* preserveIndex, bool* release); + +void store(Context* con, unsigned size, lir::Register* src, + int base, int offset, int index, unsigned scale, bool preserveIndex); + +void moveRM(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst); + +void load(Context* con, unsigned srcSize, int base, int offset, int index, + unsigned scale, unsigned dstSize, lir::Register* dst, + bool preserveIndex, bool signExtend); + +void moveMR(Context* con, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst); + +void moveZMR(Context* con, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst); + +void andR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void andC(Context* con, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void orR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void xorR(Context* con, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void moveAR2(Context* con, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst); + +void moveAR(Context* con, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst); + +void compareRR(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void compareCR(Context* con, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void compareCM(Context* con, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b); + +void compareRM(Context* con, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Memory* b); + +int32_t +branch(Context* con, lir::TernaryOperation op); + +void conditional(Context* con, int32_t branch, lir::Constant* target); + +void branch(Context* con, lir::TernaryOperation op, lir::Constant* target); + +void branchLong(Context* con, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned); + +void branchRR(Context* con, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target); + +void branchCR(Context* con, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target); + +void branchRM(Context* con, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target); + +void branchCM(Context* con, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target); + +ShiftMaskPromise* +shiftMaskPromise(Context* con, Promise* base, unsigned shift, int64_t mask); + +void moveCM(Context* con, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Memory* dst); + +void negateRR(Context* con, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Register* dst); + +void callR(Context* con, unsigned size UNUSED, lir::Register* target); + +void callC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void longCallC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void longJumpC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void jumpC(Context* con, unsigned size UNUSED, lir::Constant* target); + +void return_(Context* con); + +void trap(Context* con); + +void memoryBarrier(Context*); + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_OPERATIONS_H + diff --git a/src/codegen/target/arm/registers.h b/src/codegen/target/arm/registers.h new file mode 100644 index 0000000000..85c389b222 --- /dev/null +++ b/src/codegen/target/arm/registers.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H +#define AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H + +#include +#include + +namespace avian { +namespace codegen { +namespace arm { + + +const uint64_t MASK_LO32 = 0xffffffff; +const unsigned MASK_LO16 = 0xffff; +const unsigned MASK_LO8 = 0xff; + +const int N_GPRS = 16; +const int N_FPRS = 16; +const uint32_t GPR_MASK = 0xffff; +const uint32_t FPR_MASK = 0xffff0000; + +const uint64_t GPR_MASK64 = GPR_MASK | (uint64_t)GPR_MASK << 32; +const uint64_t FPR_MASK64 = FPR_MASK | (uint64_t)FPR_MASK << 32; + +inline bool isFpr(lir::Register* reg) { + return reg->low >= N_GPRS; +} + +inline int fpr64(int reg) { return reg - N_GPRS; } +inline int fpr64(lir::Register* reg) { return fpr64(reg->low); } +inline int fpr32(int reg) { return fpr64(reg) << 1; } +inline int fpr32(lir::Register* reg) { return fpr64(reg) << 1; } + +const int ThreadRegister = 8; +const int StackRegister = 13; +const int LinkRegister = 14; +const int ProgramCounter = 15; + +} // namespace arm +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_ARM_REGISTERS_H diff --git a/src/codegen/target/multimethod.h b/src/codegen/target/multimethod.h new file mode 100644 index 0000000000..750a02d8c8 --- /dev/null +++ b/src/codegen/target/multimethod.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_TARGET_MULTIMETHOD_H +#define AVIAN_CODEGEN_TARGET_MULTIMETHOD_H + + +namespace avian { +namespace codegen { + +class Multimethod { +public: + inline static unsigned index(lir::UnaryOperation operation, lir::OperandType operand) { + return operation + (lir::UnaryOperationCount * operand); + } +}; + +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_TARGET_MULTIMETHOD_H + diff --git a/src/codegen/target/powerpc/assembler.cpp b/src/codegen/target/powerpc/assembler.cpp new file mode 100644 index 0000000000..bea86bbcb3 --- /dev/null +++ b/src/codegen/target/powerpc/assembler.cpp @@ -0,0 +1,1008 @@ +/* Copyright (c) 2009-2012, 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 +#include +#include + +#include "avian/alloc-vector.h" +#include + +#include "encode.h" +#include "context.h" +#include "fixup.h" +#include "block.h" +#include "operations.h" +#include "multimethod.h" +#include "../multimethod.h" + +using namespace vm; +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace powerpc { + +inline int unha16(int32_t high, int32_t low) { + return ((high - ((low & 0x8000) ? 1 : 0)) << 16) | low; +} + +const RegisterFile MyRegisterFile(0xFFFFFFFF, 0); + +#ifdef __APPLE__ +const unsigned FrameFooterSize = 6; +const unsigned ReturnAddressOffset = 2; +const unsigned AlignArguments = false; +#else +const unsigned FrameFooterSize = 2; +const unsigned ReturnAddressOffset = 1; +const unsigned AlignArguments = true; +#endif + +const unsigned StackAlignmentInBytes = 16; +const unsigned StackAlignmentInWords += StackAlignmentInBytes / TargetBytesPerWord; + +const int StackRegister = 1; +const int ThreadRegister = 13; + +const bool DebugJumps = false; + +class JumpOffset; + +unsigned padding(MyBlock*, unsigned); + +bool bounded(int right, int left, int32_t v); + +class Task; +class ConstantPoolEntry; + +bool +needJump(MyBlock* b) +{ + return b->next or (not bounded(2, 16, b->size)); +} + +unsigned +padding(MyBlock* b, unsigned offset) +{ + unsigned total = 0; + for (JumpEvent* e = b->jumpEventHead; e; e = e->next) { + if (e->offset <= offset) { + for (JumpOffset* o = e->jumpOffsetHead; o; o = o->next) { + total += TargetBytesPerWord; + } + + if (needJump(b)) { + total += TargetBytesPerWord; + } + } else { + break; + } + } + + return total; +} + +void +resolve(MyBlock* b) +{ + Context* c = b->context; + + for (JumpEvent** e = &(b->jumpEventHead); *e;) { + for (JumpOffset** o = &((*e)->jumpOffsetHead); *o;) { + if ((*o)->task->promise->resolved() + and (*o)->task->instructionOffset->resolved()) + { + int32_t v = reinterpret_cast((*o)->task->promise->value()) + - (c->result + (*o)->task->instructionOffset->value()); + + if (bounded(2, 16, v)) { + // this conditional jump needs no indirection -- a direct + // jump will suffice + *o = (*o)->next; + continue; + } + } + + o = &((*o)->next); + } + + if ((*e)->jumpOffsetHead == 0) { + *e = (*e)->next; + } else { + e = &((*e)->next); + } + } + + if (b->jumpOffsetHead) { + if (c->jumpOffsetTail) { + c->jumpOffsetTail->next = b->jumpOffsetHead; + } else { + c->jumpOffsetHead = b->jumpOffsetHead; + } + c->jumpOffsetTail = b->jumpOffsetTail; + } + + if (c->jumpOffsetHead) { + bool append; + if (b->next == 0 or b->next->jumpEventHead) { + append = true; + } else { + int32_t v = (b->start + b->size + b->next->size + TargetBytesPerWord) + - (c->jumpOffsetHead->offset + c->jumpOffsetHead->block->start); + + append = not bounded(2, 16, v); + + if (DebugJumps) { + fprintf(stderr, + "current %p %d %d next %p %d %d\n", + b, b->start, b->size, b->next, b->start + b->size, + b->next->size); + fprintf(stderr, + "offset %p %d is of distance %d to next block; append? %d\n", + c->jumpOffsetHead, c->jumpOffsetHead->offset, v, append); + } + } + + if (append) { +#ifndef NDEBUG + int32_t v = (b->start + b->size) + - (c->jumpOffsetHead->offset + c->jumpOffsetHead->block->start); + + expect(c, bounded(2, 16, v)); +#endif // not NDEBUG + + appendJumpEvent(c, b, b->size, c->jumpOffsetHead, c->jumpOffsetTail); + + if (DebugJumps) { + for (JumpOffset* o = c->jumpOffsetHead; o; o = o->next) { + fprintf(stderr, + "include %p %d in jump event %p at offset %d in block %p\n", + o, o->offset, b->jumpEventTail, b->size, b); + } + } + + c->jumpOffsetHead = 0; + c->jumpOffsetTail = 0; + } + } +} + +// BEGIN OPERATION COMPILERS + +using namespace isa; + +// END OPERATION COMPILERS + +unsigned +argumentFootprint(unsigned footprint) +{ + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); +} + +void +nextFrame(ArchitectureContext* c UNUSED, int32_t* start, unsigned size, + unsigned footprint, void* link, bool, + unsigned targetParameterFootprint, void** ip, void** stack) +{ + assert(c, *ip >= start); + assert(c, *ip <= start + (size / BytesPerWord)); + + int32_t* instruction = static_cast(*ip); + + if ((*start >> 26) == 32) { + // skip stack overflow check + start += 3; + } + + if (instruction <= start + 2 + or *instruction == lwz(0, 1, 8) + or *instruction == mtlr(0) + or *instruction == blr()) + { + *ip = link; + return; + } + + unsigned offset = footprint; + + if (TailCalls) { + if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { + offset += argumentFootprint(targetParameterFootprint) + - StackAlignmentInWords; + } + + // check for post-non-tail-call stack adjustment of the form "lwzx + // r0,0(r1); stwu r0,offset(r1)": + if (instruction < start + (size / BytesPerWord) - 1 + and (static_cast(instruction[1]) >> 16) == 0x9401) + { + offset += static_cast(instruction[1]) / BytesPerWord; + } else if ((static_cast(*instruction) >> 16) == 0x9401) { + offset += static_cast(*instruction) / BytesPerWord; + } + + // todo: check for and handle tail calls + } + + *ip = static_cast(*stack)[offset + ReturnAddressOffset]; + *stack = static_cast(*stack) + offset; +} + +class MyArchitecture: public Architecture { + public: + MyArchitecture(System* system): c(system), referenceCount(0) { + populateTables(&c); + } + + virtual unsigned floatRegisterSize() { + return 0; + } + + virtual const RegisterFile* registerFile() { + return &MyRegisterFile; + } + + virtual int scratch() { + return 31; + } + + virtual int stack() { + return StackRegister; + } + + virtual int thread() { + return ThreadRegister; + } + + virtual int returnLow() { + return 4; + } + + virtual int returnHigh() { + return (TargetBytesPerWord == 4 ? 3 : lir::NoRegister); + } + + virtual int virtualCallTarget() { + return 4; + } + + virtual int virtualCallIndex() { + return 3; + } + + virtual bool bigEndian() { + return true; + } + + virtual uintptr_t maximumImmediateJump() { + return 0x1FFFFFF; + } + + virtual bool reserved(int register_) { + switch (register_) { + case 0: // r0 has special meaning in addi and other instructions + case StackRegister: + case ThreadRegister: +#ifndef __APPLE__ + // r2 is reserved for system uses on SYSV + case 2: +#endif + return true; + + default: + return false; + } + } + + virtual unsigned frameFootprint(unsigned footprint) { + return max(footprint, StackAlignmentInWords); + } + + virtual unsigned argumentFootprint(unsigned footprint) { + return powerpc::argumentFootprint(footprint); + } + + virtual bool argumentAlignment() { + return AlignArguments; + } + + virtual bool argumentRegisterAlignment() { + return true; + } + + virtual unsigned argumentRegisterCount() { + return 8; + } + + virtual int argumentRegister(unsigned index) { + assert(&c, index < argumentRegisterCount()); + + return index + 3; + } + + virtual bool hasLinkRegister() { + return true; + } + + 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(lir::UnaryOperation op UNUSED, + void* returnAddress, + void* newTarget) + { + switch (op) { + case lir::Call: + case lir::Jump: + case lir::AlignedCall: + case lir::AlignedJump: { + updateOffset(c.s, static_cast(returnAddress) - 4, false, + reinterpret_cast(newTarget), 0); + } break; + + case lir::LongCall: + case lir::LongJump: { + updateImmediate + (c.s, static_cast(returnAddress) - 12, + reinterpret_cast(newTarget), TargetBytesPerWord, false); + } break; + + case lir::AlignedLongCall: + case lir::AlignedLongJump: { + uint32_t* p = static_cast(returnAddress) - 4; + *reinterpret_cast(unha16(p[0] & 0xFFFF, p[1] & 0xFFFF)) + = newTarget; + } break; + + default: abort(&c); + } + } + + virtual unsigned constantCallSize() { + return 4; + } + + virtual void setConstant(void* dst, uint64_t constant) { + updateImmediate(c.s, dst, constant, TargetBytesPerWord, false); + } + + virtual unsigned alignFrameSize(unsigned sizeInWords) { + const unsigned alignment = StackAlignmentInWords; + return (ceilingDivide(sizeInWords + FrameFooterSize, alignment) * alignment); + } + + virtual void nextFrame(void* start, unsigned size, unsigned footprint, + void* link, bool mostRecent, + unsigned targetParameterFootprint, void** ip, + void** stack) + { + powerpc::nextFrame(&c, static_cast(start), size, footprint, link, + mostRecent, targetParameterFootprint, ip, stack); + } + + virtual void* frameIp(void* stack) { + return stack ? static_cast(stack)[ReturnAddressOffset] : 0; + } + + virtual unsigned frameHeaderSize() { + return 0; + } + + virtual unsigned frameReturnAddressSize() { + return 0; + } + + virtual unsigned frameFooterSize() { + return FrameFooterSize; + } + + virtual int returnAddressOffset() { + return ReturnAddressOffset; + } + + virtual int framePointerOffset() { + return 0; + } + + virtual bool alwaysCondensed(lir::BinaryOperation) { + return false; + } + + virtual bool alwaysCondensed(lir::TernaryOperation) { + return false; + } + + virtual void plan + (lir::UnaryOperation, + unsigned, OperandMask& aMask, + bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + aMask.registerMask = ~static_cast(0); + *thunk = false; + } + + virtual void planSource + (lir::BinaryOperation op, + unsigned, OperandMask& aMask, + unsigned, bool* thunk) + { + aMask.typeMask = ~0; + aMask.registerMask = ~static_cast(0); + + *thunk = false; + + switch (op) { + case lir::Negate: + aMask.typeMask = (1 << lir::RegisterOperand); + break; + + case lir::Absolute: + case lir::FloatAbsolute: + case lir::FloatSquareRoot: + case lir::FloatNegate: + case lir::Float2Float: + case lir::Float2Int: + case lir::Int2Float: + *thunk = true; + break; + + default: + break; + } + } + + virtual void planDestination + (lir::BinaryOperation op, + unsigned, const OperandMask& aMask UNUSED, + unsigned, OperandMask& bMask) + { + bMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + bMask.registerMask = ~static_cast(0); + + switch (op) { + case lir::Negate: + bMask.typeMask = (1 << lir::RegisterOperand); + break; + + default: + break; + } + } + + virtual void planMove + (unsigned, OperandMask& srcMask, + OperandMask& tmpMask, + const OperandMask& dstMask) + { + srcMask.typeMask = ~0; + srcMask.registerMask = ~static_cast(0); + + tmpMask.typeMask = 0; + tmpMask.registerMask = 0; + + if (dstMask.typeMask & (1 << lir::MemoryOperand)) { + // can't move directly from memory or constant to memory + srcMask.typeMask = 1 << lir::RegisterOperand; + tmpMask.typeMask = 1 << lir::RegisterOperand; + tmpMask.registerMask = ~static_cast(0); + } + } + + virtual void planSource + (lir::TernaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned, OperandMask& bMask, + unsigned, bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + aMask.registerMask = ~static_cast(0); + + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = ~static_cast(0); + + *thunk = false; + + switch (op) { + case lir::Add: + case lir::Subtract: + if (aSize == 8) { + aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + } + break; + + case lir::Multiply: + aMask.typeMask = bMask.typeMask = (1 << lir::RegisterOperand); + break; + + case lir::Divide: + case lir::Remainder: + // todo: we shouldn't need to defer to thunks for integers which + // are smaller than or equal to tne native word size, but + // PowerPC doesn't generate traps for divide by zero, so we'd + // need to do the checks ourselves. Using an inline check + // should be faster than calling an out-of-line thunk, but the + // thunk is easier, so they's what we do for now. + if (true) {//if (TargetBytesPerWord == 4 and aSize == 8) { + *thunk = true; + } else { + aMask.typeMask = (1 << lir::RegisterOperand); + } + break; + + case lir::FloatAdd: + case lir::FloatSubtract: + case lir::FloatMultiply: + case lir::FloatDivide: + case lir::FloatRemainder: + case lir::JumpIfFloatEqual: + case lir::JumpIfFloatNotEqual: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatLessOrUnordered: + case lir::JumpIfFloatGreaterOrUnordered: + case lir::JumpIfFloatLessOrEqualOrUnordered: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + *thunk = true; + break; + + default: + break; + } + } + + virtual void planDestination + (lir::TernaryOperation op, + unsigned, const OperandMask& aMask UNUSED, + unsigned, const OperandMask& bMask UNUSED, + unsigned, OperandMask& cMask) + { + if (isBranch(op)) { + cMask.typeMask = (1 << lir::ConstantOperand); + cMask.registerMask = 0; + } else { + cMask.typeMask = (1 << lir::RegisterOperand); + cMask.registerMask = ~static_cast(0); + } + } + + virtual Assembler* makeAssembler(Allocator* allocator, Zone* zone); + + virtual void acquire() { + ++ referenceCount; + } + + virtual void release() { + if (-- referenceCount == 0) { + c.s->free(this); + } + } + + ArchitectureContext c; + unsigned referenceCount; +}; + +class MyAssembler: public Assembler { + public: + MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): + c(s, a, zone), arch_(arch) + { } + + virtual void setClient(Client* client) { + assert(&c, c.client == 0); + c.client = client; + } + + virtual Architecture* arch() { + return arch_; + } + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) + { + lir::Register stack(StackRegister); + lir::Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); + lir::Constant handlerConstant + (new(c.zone) ResolvedPromise(handler)); + branchRM(&c, lir::JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, + &handlerConstant); + } + + virtual void saveFrame(unsigned stackOffset, unsigned) { + lir::Register returnAddress(0); + emit(&c, mflr(returnAddress.low)); + + lir::Memory returnAddressDst + (StackRegister, ReturnAddressOffset * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, + &returnAddressDst); + + lir::Register stack(StackRegister); + lir::Memory stackDst(ThreadRegister, stackOffset); + moveRM(&c, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); + } + + virtual void pushFrame(unsigned argumentCount, ...) { + struct { + unsigned size; + lir::OperandType type; + lir::Operand* operand; + } arguments[argumentCount]; + + va_list a; va_start(a, argumentCount); + unsigned footprint = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + arguments[i].size = va_arg(a, unsigned); + arguments[i].type = static_cast(va_arg(a, int)); + arguments[i].operand = va_arg(a, lir::Operand*); + footprint += ceilingDivide(arguments[i].size, TargetBytesPerWord); + } + va_end(a); + + allocateFrame(arch_->alignFrameSize(footprint)); + + unsigned offset = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + if (i < arch_->argumentRegisterCount()) { + lir::Register dst(arch_->argumentRegister(i)); + + apply(lir::Move, + OperandInfo(arguments[i].size, arguments[i].type, arguments[i].operand), + OperandInfo(pad(arguments[i].size, TargetBytesPerWord), lir::RegisterOperand, + &dst)); + + offset += ceilingDivide(arguments[i].size, TargetBytesPerWord); + } else { + lir::Memory dst + (ThreadRegister, (offset + FrameFooterSize) * TargetBytesPerWord); + + apply(lir::Move, + OperandInfo(arguments[i].size, arguments[i].type, arguments[i].operand), + OperandInfo(pad(arguments[i].size, TargetBytesPerWord), lir::MemoryOperand, &dst)); + + offset += ceilingDivide(arguments[i].size, TargetBytesPerWord); + } + } + } + + virtual void allocateFrame(unsigned footprint) { + lir::Register returnAddress(0); + emit(&c, mflr(returnAddress.low)); + + lir::Memory returnAddressDst + (StackRegister, ReturnAddressOffset * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, + &returnAddressDst); + + lir::Register stack(StackRegister); + lir::Memory stackDst(StackRegister, -footprint * TargetBytesPerWord); + moveAndUpdateRM + (&c, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); + } + + virtual void adjustFrame(unsigned difference) { + lir::Register nextStack(0); + lir::Memory stackSrc(StackRegister, 0); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &nextStack); + + lir::Memory stackDst(StackRegister, -difference * TargetBytesPerWord); + moveAndUpdateRM + (&c, TargetBytesPerWord, &nextStack, TargetBytesPerWord, &stackDst); + } + + virtual void popFrame(unsigned) { + lir::Register stack(StackRegister); + lir::Memory stackSrc(StackRegister, 0); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack); + + lir::Register returnAddress(0); + lir::Memory returnAddressSrc + (StackRegister, ReturnAddressOffset * TargetBytesPerWord); + moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, + &returnAddress); + + emit(&c, mtlr(returnAddress.low)); + } + + virtual void popFrameForTailCall(unsigned footprint, + int offset, + int returnAddressSurrogate, + int framePointerSurrogate) + { + if (TailCalls) { + if (offset) { + lir::Register tmp(0); + lir::Memory returnAddressSrc + (StackRegister, (ReturnAddressOffset + footprint) + * TargetBytesPerWord); + moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, + &tmp); + + emit(&c, mtlr(tmp.low)); + + lir::Memory stackSrc(StackRegister, footprint * TargetBytesPerWord); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp); + + lir::Memory stackDst + (StackRegister, (footprint - offset) * TargetBytesPerWord); + moveAndUpdateRM + (&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, &stackDst); + + if (returnAddressSurrogate != lir::NoRegister) { + assert(&c, offset > 0); + + lir::Register ras(returnAddressSurrogate); + lir::Memory dst + (StackRegister, (ReturnAddressOffset + offset) + * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); + } + + if (framePointerSurrogate != lir::NoRegister) { + assert(&c, offset > 0); + + lir::Register fps(framePointerSurrogate); + lir::Memory dst(StackRegister, offset * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst); + } + } else { + popFrame(footprint); + } + } else { + abort(&c); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + { + popFrame(frameFootprint); + + assert(&c, argumentFootprint >= StackAlignmentInWords); + assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); + + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + lir::Register tmp(0); + lir::Memory stackSrc(StackRegister, 0); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp); + + lir::Memory stackDst(StackRegister, + (argumentFootprint - StackAlignmentInWords) + * TargetBytesPerWord); + moveAndUpdateRM + (&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, &stackDst); + } + + return_(&c); + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + { + popFrame(frameFootprint); + + lir::Register tmp1(0); + lir::Memory stackSrc(StackRegister, 0); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp1); + + lir::Register tmp2(5); + lir::Memory newStackSrc(ThreadRegister, stackOffsetFromThread); + moveMR(&c, TargetBytesPerWord, &newStackSrc, TargetBytesPerWord, &tmp2); + + lir::Register stack(StackRegister); + subR(&c, TargetBytesPerWord, &stack, &tmp2, &tmp2); + + lir::Memory stackDst(StackRegister, 0, tmp2.low); + moveAndUpdateRM + (&c, TargetBytesPerWord, &tmp1, TargetBytesPerWord, &stackDst); + + return_(&c); + } + + virtual void apply(lir::Operation op) { + arch_->c.operations[op](&c); + } + + virtual void apply(lir::UnaryOperation op, OperandInfo a) + { + arch_->c.unaryOperations[Multimethod::index(op, a.type)] + (&c, a.size, a.operand); + } + + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) + { + arch_->c.binaryOperations[index(&(arch_->c), op, a.type, b.type)] + (&c, a.size, a.operand, b.size, b.operand); + } + + virtual void apply(lir::TernaryOperation op, OperandInfo a, OperandInfo b, OperandInfo c) + { + if (isBranch(op)) { + assert(&this->c, a.size == b.size); + assert(&this->c, c.size == TargetBytesPerWord); + assert(&this->c, c.type == lir::ConstantOperand); + + arch_->c.branchOperations[branchIndex(&(arch_->c), a.type, b.type)] + (&this->c, op, a.size, a.operand, b.operand, c.operand); + } else { + assert(&this->c, b.size == c.size); + assert(&this->c, b.type == lir::RegisterOperand); + assert(&this->c, c.type == lir::RegisterOperand); + + arch_->c.ternaryOperations[index(&(arch_->c), op, a.type)] + (&this->c, b.size, a.operand, b.operand, c.operand); + } + } + + virtual void setDestination(uint8_t* dst) { + c.result = dst; + } + + virtual void write() { + uint8_t* dst = c.result; + unsigned dstOffset = 0; + for (MyBlock* b = c.firstBlock; b; b = b->next) { + if (DebugJumps) { + fprintf(stderr, "write block %p\n", b); + } + + unsigned blockOffset = 0; + for (JumpEvent* e = b->jumpEventHead; e; e = e->next) { + unsigned size = e->offset - blockOffset; + memcpy(dst + dstOffset, c.code.data + b->offset + blockOffset, size); + blockOffset = e->offset; + dstOffset += size; + + unsigned jumpTableSize = 0; + for (JumpOffset* o = e->jumpOffsetHead; o; o = o->next) { + if (DebugJumps) { + fprintf(stderr, "visit offset %p %d in block %p\n", + o, o->offset, b); + } + + uint8_t* address = dst + dstOffset + jumpTableSize; + + if (needJump(b)) { + address += TargetBytesPerWord; + } + + o->task->jumpAddress = address; + + jumpTableSize += TargetBytesPerWord; + } + + assert(&c, jumpTableSize); + + bool jump = needJump(b); + if (jump) { + write4(dst + dstOffset, isa::b(jumpTableSize + TargetBytesPerWord)); + } + + dstOffset += jumpTableSize + (jump ? TargetBytesPerWord : 0); + } + + unsigned size = b->size - blockOffset; + + memcpy(dst + dstOffset, + c.code.data + b->offset + blockOffset, + size); + + dstOffset += size; + } + + unsigned index = dstOffset; + assert(&c, index % TargetBytesPerWord == 0); + for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { + e->address = dst + index; + index += TargetBytesPerWord; + } + + for (Task* t = c.tasks; t; t = t->next) { + t->run(&c); + } + + for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { + *static_cast(e->address) = e->constant->value(); +// fprintf(stderr, "constant %p at %p\n", reinterpret_cast(e->constant->value()), e->address); + } + } + + virtual Promise* offset(bool) { + return powerpc::offsetPromise(&c); + } + + virtual Block* endBlock(bool startNew) { + MyBlock* b = c.lastBlock; + b->size = c.code.length() - b->offset; + if (startNew) { + c.lastBlock = new(c.zone) MyBlock(&c, c.code.length()); + } else { + c.lastBlock = 0; + } + return b; + } + + virtual void endEvent() { + MyBlock* b = c.lastBlock; + unsigned thisEventOffset = c.code.length() - b->offset; + if (b->jumpOffsetHead) { + int32_t v = (thisEventOffset + TargetBytesPerWord) + - b->jumpOffsetHead->offset; + + if (v > 0 and not bounded(2, 16, v)) { + appendJumpEvent + (&c, b, b->lastEventOffset, b->jumpOffsetHead, + b->lastJumpOffsetTail); + + if (DebugJumps) { + for (JumpOffset* o = b->jumpOffsetHead; + o != b->lastJumpOffsetTail->next; o = o->next) + { + fprintf(stderr, + "in endEvent, include %p %d in jump event %p " + "at offset %d in block %p\n", + o, o->offset, b->jumpEventTail, b->lastEventOffset, b); + } + } + + b->jumpOffsetHead = b->lastJumpOffsetTail->next; + b->lastJumpOffsetTail->next = 0; + if (b->jumpOffsetHead == 0) { + b->jumpOffsetTail = 0; + } + } + } + b->lastEventOffset = thisEventOffset; + b->lastJumpOffsetTail = b->jumpOffsetTail; + } + + virtual unsigned length() { + return c.code.length(); + } + + virtual unsigned footerSize() { + return c.constantPoolCount * TargetBytesPerWord; + } + + virtual void dispose() { + c.code.dispose(); + } + + Context c; + MyArchitecture* arch_; +}; + +Assembler* MyArchitecture::makeAssembler(Allocator* allocator, Zone* zone) { + return new(zone) MyAssembler(this->c.s, allocator, zone, this); +} + +} // namespace powerpc + +Architecture* +makeArchitecturePowerpc(System* system, bool) +{ + return new (allocate(system, sizeof(powerpc::MyArchitecture))) powerpc::MyArchitecture(system); +} + +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/block.cpp b/src/codegen/target/powerpc/block.cpp new file mode 100644 index 0000000000..28fae01664 --- /dev/null +++ b/src/codegen/target/powerpc/block.cpp @@ -0,0 +1,42 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" +#include "avian/common.h" + +namespace avian { +namespace codegen { +namespace powerpc { + +void resolve(MyBlock*); + +unsigned padding(MyBlock*, unsigned); + +MyBlock::MyBlock(Context* context, unsigned offset): + context(context), next(0), jumpOffsetHead(0), jumpOffsetTail(0), + lastJumpOffsetTail(0), jumpEventHead(0), jumpEventTail(0), + lastEventOffset(0), offset(offset), start(~0), size(0), resolved(false) +{ } + +unsigned MyBlock::resolve(unsigned start, Assembler::Block* next) { + this->start = start; + this->next = static_cast(next); + + powerpc::resolve(this); + + this->resolved = true; + + return start + size + padding(this, size); +} + +} // namespace powerpc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/block.h b/src/codegen/target/powerpc/block.h new file mode 100644 index 0000000000..e0dd563ad7 --- /dev/null +++ b/src/codegen/target/powerpc/block.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_BLOCK_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_BLOCK_H + +namespace avian { +namespace codegen { +namespace powerpc { + +class JumpEvent; + +class MyBlock: public Assembler::Block { + public: + MyBlock(Context* context, unsigned offset); + + virtual unsigned resolve(unsigned start, Assembler::Block* next); + + Context* context; + MyBlock* next; + JumpOffset* jumpOffsetHead; + JumpOffset* jumpOffsetTail; + JumpOffset* lastJumpOffsetTail; + JumpEvent* jumpEventHead; + JumpEvent* jumpEventTail; + unsigned lastEventOffset; + unsigned offset; + unsigned start; + unsigned size; + bool resolved; +}; + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_BLOCK_H diff --git a/src/codegen/target/powerpc/context.cpp b/src/codegen/target/powerpc/context.cpp new file mode 100644 index 0000000000..bf0aa13d4b --- /dev/null +++ b/src/codegen/target/powerpc/context.cpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" +#include "avian/common.h" + +namespace avian { +namespace codegen { +namespace powerpc { + + +Context::Context(vm::System* s, vm::Allocator* a, vm::Zone* zone): + s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), + firstBlock(new(zone) MyBlock(this, 0)), + lastBlock(firstBlock), jumpOffsetHead(0), jumpOffsetTail(0), + constantPool(0), constantPoolCount(0) +{ } + +} // namespace powerpc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/context.h b/src/codegen/target/powerpc/context.h new file mode 100644 index 0000000000..7726b88cf5 --- /dev/null +++ b/src/codegen/target/powerpc/context.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_CONTEXT_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_CONTEXT_H + +#include +#include "avian/alloc-vector.h" + +#ifdef powerpc +#undef powerpc +#endif + +namespace vm { +class System; +class Allocator; +class Zone; +} // namespace vm + + +namespace avian { +namespace codegen { +namespace powerpc { + +class Task; +class JumpOffset; +class ConstantPoolEntry; +class MyBlock; + +class Context { + public: + Context(vm::System* s, vm::Allocator* a, vm::Zone* zone); + + vm::System* s; + vm::Zone* zone; + Assembler::Client* client; + vm::Vector code; + Task* tasks; + uint8_t* result; + MyBlock* firstBlock; + MyBlock* lastBlock; + JumpOffset* jumpOffsetHead; + JumpOffset* jumpOffsetTail; + ConstantPoolEntry* constantPool; + unsigned constantPoolCount; +}; + +typedef void (*OperationType)(Context*); + +typedef void (*UnaryOperationType)(Context*, unsigned, lir::Operand*); + +typedef void (*BinaryOperationType) +(Context*, unsigned, lir::Operand*, unsigned, lir::Operand*); + +typedef void (*TernaryOperationType) +(Context*, unsigned, lir::Operand*, lir::Operand*, + lir::Operand*); + +typedef void (*BranchOperationType) +(Context*, lir::TernaryOperation, unsigned, lir::Operand*, + lir::Operand*, lir::Operand*); + +class ArchitectureContext { + public: + ArchitectureContext(vm::System* s): s(s) { } + + vm::System* s; + OperationType operations[lir::OperationCount]; + UnaryOperationType unaryOperations[lir::UnaryOperationCount + * lir::OperandTypeCount]; + BinaryOperationType binaryOperations + [lir::BinaryOperationCount * lir::OperandTypeCount * lir::OperandTypeCount]; + TernaryOperationType ternaryOperations + [lir::NonBranchTernaryOperationCount * lir::OperandTypeCount]; + BranchOperationType branchOperations + [lir::BranchOperationCount * lir::OperandTypeCount * lir::OperandTypeCount]; +}; + +inline avian::util::Aborter* getAborter(Context* con) { + return con->s; +} + +inline avian::util::Aborter* getAborter(ArchitectureContext* con) { + return con->s; +} + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_CONTEXT_H diff --git a/src/codegen/target/powerpc/encode.h b/src/codegen/target/powerpc/encode.h new file mode 100644 index 0000000000..811ddce402 --- /dev/null +++ b/src/codegen/target/powerpc/encode.h @@ -0,0 +1,141 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_ENCODE_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_ENCODE_H + +#ifdef powerpc +#undef powerpc +#endif + +namespace avian { +namespace codegen { +namespace powerpc { + +namespace isa { +// INSTRUCTION FORMATS +inline int D(int op, int rt, int ra, int d) { return op<<26|rt<<21|ra<<16|(d & 0xFFFF); } +// inline int DS(int op, int rt, int ra, int ds, int xo) { return op<<26|rt<<21|ra<<16|ds<<2|xo; } +inline int I(int op, int li, int aa, int lk) { return op<<26|(li & 0x3FFFFFC)|aa<<1|lk; } +inline int B(int op, int bo, int bi, int bd, int aa, int lk) { return op<<26|bo<<21|bi<<16|(bd & 0xFFFC)|aa<<1|lk; } +// inline int SC(int op, int lev) { return op<<26|lev<<5|2; } +inline int X(int op, int rt, int ra, int rb, int xo, int rc) { return op<<26|rt<<21|ra<<16|rb<<11|xo<<1|rc; } +inline int XL(int op, int bt, int ba, int bb, int xo, int lk) { return op<<26|bt<<21|ba<<16|bb<<11|xo<<1|lk; } +inline int XFX(int op, int rt, int spr, int xo) { return op<<26|rt<<21|((spr >> 5) | ((spr << 5) & 0x3E0))<<11|xo<<1; } +// inline int XFL(int op, int flm, int frb, int xo, int rc) { return op<<26|flm<<17|frb<<11|xo<<1|rc; } +// inline int XS(int op, int rs, int ra, int sh, int xo, int sh2, int rc) { return op<<26|rs<<21|ra<<16|sh<<11|xo<<2|sh2<<1|rc; } +inline int XO(int op, int rt, int ra, int rb, int oe, int xo, int rc) { return op<<26|rt<<21|ra<<16|rb<<11|oe<<10|xo<<1|rc; } +// inline int A(int op, int frt, int fra, int frb, int frc, int xo, int rc) { return op<<26|frt<<21|fra<<16|frb<<11|frc<<6|xo<<1|rc; } +inline int M(int op, int rs, int ra, int rb, int mb, int me, int rc) { return op<<26|rs<<21|ra<<16|rb<<11|mb<<6|me<<1|rc; } +// inline int MD(int op, int rs, int ra, int sh, int mb, int xo, int sh2, int rc) { return op<<26|rs<<21|ra<<16|sh<<11|mb<<5|xo<<2|sh2<<1|rc; } +// inline int MDS(int op, int rs, int ra, int rb, int mb, int xo, int rc) { return op<<26|rs<<21|ra<<16|rb<<11|mb<<5|xo<<1|rc; } +// INSTRUCTIONS +inline int lbz(int rt, int ra, int i) { return D(34, rt, ra, i); } +inline int lbzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 87, 0); } +inline int lha(int rt, int ra, int i) { return D(42, rt, ra, i); } +inline int lhax(int rt, int ra, int rb) { return X(31, rt, ra, rb, 343, 0); } +// inline int lhz(int rt, int ra, int i) { return D(40, rt, ra, i); } +inline int lhzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 279, 0); } +inline int lwz(int rt, int ra, int i) { return D(32, rt, ra, i); } +inline int lwzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 23, 0); } +inline int stb(int rs, int ra, int i) { return D(38, rs, ra, i); } +inline int stbx(int rs, int ra, int rb) { return X(31, rs, ra, rb, 215, 0); } +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); } +inline int adde(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 138, 0); } +inline int addi(int rt, int ra, int i) { return D(14, rt, ra, i); } +inline int addic(int rt, int ra, int i) { return D(12, rt, ra, i); } +inline int addis(int rt, int ra, int i) { return D(15, rt, ra, i); } +inline int subf(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 40, 0); } +inline int subfc(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 8, 0); } +inline int subfe(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 136, 0); } +inline int subfic(int rt, int ra, int i) { return D(8, rt, ra, i); } +inline int subfze(int rt, int ra) { return XO(31, rt, ra, 0, 0, 200, 0); } +inline int mullw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 235, 0); } +// inline int mulhw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 75, 0); } +inline int mulhwu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 11, 0); } +// inline int mulli(int rt, int ra, int i) { return D(7, rt, ra, i); } +inline int divw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 491, 0); } +// inline int divwu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 459, 0); } +// inline int divd(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 489, 0); } +// inline int divdu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 457, 0); } +inline int neg(int rt, int ra) { return XO(31, rt, ra, 0, 0, 104, 0); } +inline int and_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 28, 0); } +inline int andi(int rt, int ra, int i) { return D(28, ra, rt, i); } +inline int andis(int rt, int ra, int i) { return D(29, ra, rt, i); } +inline int or_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 444, 0); } +inline int ori(int rt, int ra, int i) { return D(24, rt, ra, i); } +inline int xor_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 316, 0); } +inline int oris(int rt, int ra, int i) { return D(25, rt, ra, i); } +inline int xori(int rt, int ra, int i) { return D(26, rt, ra, i); } +inline int xoris(int rt, int ra, int i) { return D(27, rt, ra, i); } +inline int rlwinm(int rt, int ra, int i, int mb, int me) { return M(21, ra, rt, i, mb, me, 0); } +inline int rlwimi(int rt, int ra, int i, int mb, int me) { return M(20, ra, rt, i, mb, me, 0); } +inline int slw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 24, 0); } +// inline int sld(int rt, int ra, int rb) { return X(31, ra, rt, rb, 27, 0); } +inline int srw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 536, 0); } +inline int sraw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 792, 0); } +inline int srawi(int rt, int ra, int sh) { return X(31, ra, rt, sh, 824, 0); } +inline int extsb(int rt, int rs) { return X(31, rs, rt, 0, 954, 0); } +inline int extsh(int rt, int rs) { return X(31, rs, rt, 0, 922, 0); } +inline int mfspr(int rt, int spr) { return XFX(31, rt, spr, 339); } +inline int mtspr(int spr, int rs) { return XFX(31, rs, spr, 467); } +inline int b(int i) { return I(18, i, 0, 0); } +inline int bl(int i) { return I(18, i, 0, 1); } +inline int bcctr(int bo, int bi, int lk) { return XL(19, bo, bi, 0, 528, lk); } +inline int bclr(int bo, int bi, int lk) { return XL(19, bo, bi, 0, 16, lk); } +inline int bc(int bo, int bi, int bd, int lk) { return B(16, bo, bi, bd, 0, lk); } +inline int cmp(int bf, int ra, int rb) { return X(31, bf << 2, ra, rb, 0, 0); } +inline int cmpl(int bf, int ra, int rb) { return X(31, bf << 2, ra, rb, 32, 0); } +inline int cmpi(int bf, int ra, int i) { return D(11, bf << 2, ra, i); } +inline int cmpli(int bf, int ra, int i) { return D(10, bf << 2, ra, i); } +inline int sync(int L) { return X(31, L, 0, 0, 598, 0); } +// PSEUDO-INSTRUCTIONS +inline int li(int rt, int i) { return addi(rt, 0, i); } +inline int lis(int rt, int i) { return addis(rt, 0, i); } +inline int slwi(int rt, int ra, int i) { return rlwinm(rt, ra, i, 0, 31-i); } +inline int srwi(int rt, int ra, int i) { return rlwinm(rt, ra, 32-i, i, 31); } +// inline int sub(int rt, int ra, int rb) { return subf(rt, rb, ra); } +// inline int subc(int rt, int ra, int rb) { return subfc(rt, rb, ra); } +// inline int subi(int rt, int ra, int i) { return addi(rt, ra, -i); } +// inline int subis(int rt, int ra, int i) { return addis(rt, ra, -i); } +inline int mr(int rt, int ra) { return or_(rt, ra, ra); } +inline int mflr(int rx) { return mfspr(rx, 8); } +inline int mtlr(int rx) { return mtspr(8, rx); } +inline int mtctr(int rd) { return mtspr(9, rd); } +inline int bctr() { return bcctr(20, 0, 0); } +inline int bctrl() { return bcctr(20, 0, 1); } +inline int blr() { return bclr(20, 0, 0); } +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 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); } +inline int cmpwi(int ra, int i) { return cmpi(0, ra, i); } +inline int cmplwi(int ra, int i) { return cmpli(0, ra, i); } +inline int trap() { return 0x7fe00008; } // todo: macro-ify + +} // namespace isa + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_ENCODE_H + diff --git a/src/codegen/target/powerpc/fixup.cpp b/src/codegen/target/powerpc/fixup.cpp new file mode 100644 index 0000000000..6de1698fe2 --- /dev/null +++ b/src/codegen/target/powerpc/fixup.cpp @@ -0,0 +1,243 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" +#include "fixup.h" +#include "encode.h" + +namespace avian { +namespace codegen { +namespace powerpc { + +using namespace isa; +using namespace util; + +unsigned padding(MyBlock*, unsigned); + +int ha16(int32_t i); + +bool bounded(int right, int left, int32_t v) { + return ((v << left) >> left) == v and ((v >> right) << right) == v; +} + +OffsetPromise::OffsetPromise(Context* c, MyBlock* block, unsigned offset): + c(c), block(block), offset(offset) +{ } + +bool OffsetPromise::resolved() { + return block->resolved; +} + +int64_t OffsetPromise::value() { + assert(c, resolved()); + + unsigned o = offset - block->offset; + return block->start + padding(block, o) + o; +} + +Promise* offsetPromise(Context* c) { + return new(c->zone) OffsetPromise(c, c->lastBlock, c->code.length()); +} + +void* updateOffset(vm::System* s, uint8_t* instruction, bool conditional, int64_t value, + void* jumpAddress) +{ + int32_t v = reinterpret_cast(value) - instruction; + + int32_t mask; + if (conditional) { + if (not bounded(2, 16, v)) { + *static_cast(jumpAddress) = isa::b(0); + updateOffset(s, static_cast(jumpAddress), false, value, 0); + + v = static_cast(jumpAddress) - instruction; + + expect(s, bounded(2, 16, v)); + } + mask = 0xFFFC; + } else { + expect(s, bounded(2, 6, v)); + mask = 0x3FFFFFC; + } + + int32_t* p = reinterpret_cast(instruction); + *p = vm::targetV4((v & mask) | ((~mask) & vm::targetV4(*p))); + + return instruction + 4; +} + +OffsetListener::OffsetListener(vm::System* s, uint8_t* instruction, bool conditional, + void* jumpAddress): + s(s), + instruction(instruction), + jumpAddress(jumpAddress), + conditional(conditional) +{ } + +bool OffsetListener::resolve(int64_t value, void** location) { + void* p = updateOffset(s, instruction, conditional, value, jumpAddress); + if (location) *location = p; + return false; +} + +OffsetTask::OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, + bool conditional): + Task(next), + promise(promise), + instructionOffset(instructionOffset), + jumpAddress(0), + conditional(conditional) +{ } + +void OffsetTask::run(Context* c) { + if (promise->resolved()) { + updateOffset + (c->s, c->result + instructionOffset->value(), conditional, + promise->value(), jumpAddress); + } else { + new (promise->listen(sizeof(OffsetListener))) + OffsetListener(c->s, c->result + instructionOffset->value(), + conditional, jumpAddress); + } +} + +JumpOffset::JumpOffset(MyBlock* block, OffsetTask* task, unsigned offset): + block(block), task(task), next(0), offset(offset) +{ } + +JumpEvent::JumpEvent(JumpOffset* jumpOffsetHead, JumpOffset* jumpOffsetTail, + unsigned offset): + jumpOffsetHead(jumpOffsetHead), jumpOffsetTail(jumpOffsetTail), next(0), + offset(offset) +{ } + +void appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, + bool conditional) +{ + OffsetTask* task = new(c->zone) OffsetTask(c->tasks, promise, instructionOffset, conditional); + + c->tasks = task; + + if (conditional) { + JumpOffset* offset = + new(c->zone) JumpOffset(c->lastBlock, task, c->code.length() - c->lastBlock->offset); + + if (c->lastBlock->jumpOffsetTail) { + c->lastBlock->jumpOffsetTail->next = offset; + } else { + c->lastBlock->jumpOffsetHead = offset; + } + c->lastBlock->jumpOffsetTail = offset; + } +} + +void appendJumpEvent(Context* c, MyBlock* b, unsigned offset, JumpOffset* head, + JumpOffset* tail) +{ + JumpEvent* e = new(c->zone) JumpEvent + (head, tail, offset); + + if (b->jumpEventTail) { + b->jumpEventTail->next = e; + } else { + b->jumpEventHead = e; + } + b->jumpEventTail = e; +} + +ShiftMaskPromise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) { + return new (c->zone) ShiftMaskPromise(base, shift, mask); +} + +void +updateImmediate(vm::System* s, void* dst, int32_t src, unsigned size, bool address) +{ + switch (size) { + case 4: { + int32_t* p = static_cast(dst); + int r = (vm::targetV4(p[1]) >> 21) & 31; + + if (address) { + p[0] = vm::targetV4(lis(r, ha16(src))); + p[1] |= vm::targetV4(src & 0xFFFF); + } else { + p[0] = vm::targetV4(lis(r, src >> 16)); + p[1] = vm::targetV4(ori(r, r, src)); + } + } break; + + default: abort(s); + } +} + +ImmediateListener::ImmediateListener(vm::System* s, void* dst, unsigned size, unsigned offset, + bool address): + s(s), dst(dst), size(size), offset(offset), address(address) +{ } + +bool ImmediateListener::resolve(int64_t value, void** location) { + updateImmediate(s, dst, value, size, address); + if (location) *location = static_cast(dst) + offset; + return false; +} + +ImmediateTask::ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, + unsigned promiseOffset, bool address): + Task(next), + promise(promise), + offset(offset), + size(size), + promiseOffset(promiseOffset), + address(address) +{ } + +void ImmediateTask::run(Context* c) { + if (promise->resolved()) { + updateImmediate + (c->s, c->result + offset->value(), promise->value(), size, address); + } else { + new (promise->listen(sizeof(ImmediateListener))) ImmediateListener + (c->s, c->result + offset->value(), size, promiseOffset, address); + } +} + +void +appendImmediateTask(Context* c, Promise* promise, Promise* offset, + unsigned size, unsigned promiseOffset, bool address) +{ + c->tasks = new(c->zone) ImmediateTask(c->tasks, promise, offset, size, promiseOffset, address); +} + +ConstantPoolEntry::ConstantPoolEntry(Context* c, Promise* constant): + c(c), constant(constant), next(c->constantPool), address(0) +{ + c->constantPool = this; + ++ c->constantPoolCount; +} + +int64_t ConstantPoolEntry::value() { + assert(c, resolved()); + + return reinterpret_cast(address); +} + +bool ConstantPoolEntry::resolved() { + return address != 0; +} + +ConstantPoolEntry* appendConstantPoolEntry(Context* c, Promise* constant) { + return new (c->zone) ConstantPoolEntry(c, constant); +} + + +} // namespace powerpc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/fixup.h b/src/codegen/target/powerpc/fixup.h new file mode 100644 index 0000000000..a51e5f6d5b --- /dev/null +++ b/src/codegen/target/powerpc/fixup.h @@ -0,0 +1,160 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_FIXUP_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_FIXUP_H + +namespace avian { +namespace codegen { +namespace powerpc { + + +class Task { + public: + Task(Task* next): next(next) { } + + virtual void run(Context* c) = 0; + + Task* next; +}; + +class OffsetPromise: public Promise { + public: + OffsetPromise(Context* c, MyBlock* block, unsigned offset); + + virtual bool resolved(); + + virtual int64_t value(); + + Context* c; + MyBlock* block; + unsigned offset; +}; + +Promise* offsetPromise(Context* c); + +void* +updateOffset(vm::System* s, uint8_t* instruction, bool conditional, int64_t value, + void* jumpAddress); + +class OffsetListener: public Promise::Listener { + public: + OffsetListener(vm::System* s, uint8_t* instruction, bool conditional, + void* jumpAddress); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + uint8_t* instruction; + void* jumpAddress; + bool conditional; +}; + +class OffsetTask: public Task { + public: + OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, + bool conditional); + + virtual void run(Context* c); + + Promise* promise; + Promise* instructionOffset; + void* jumpAddress; + bool conditional; +}; + +class JumpOffset { + public: + JumpOffset(MyBlock* block, OffsetTask* task, unsigned offset); + + MyBlock* block; + OffsetTask* task; + JumpOffset* next; + unsigned offset; +}; + +class JumpEvent { + public: + JumpEvent(JumpOffset* jumpOffsetHead, JumpOffset* jumpOffsetTail, + unsigned offset); + + JumpOffset* jumpOffsetHead; + JumpOffset* jumpOffsetTail; + JumpEvent* next; + unsigned offset; +}; + +void appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, + bool conditional); + +void appendJumpEvent(Context* c, MyBlock* b, unsigned offset, JumpOffset* head, + JumpOffset* tail); + +ShiftMaskPromise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask); + +void updateImmediate(vm::System* s, void* dst, int32_t src, unsigned size, bool address); + +class ImmediateListener: public Promise::Listener { + public: + ImmediateListener(vm::System* s, void* dst, unsigned size, unsigned offset, + bool address); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + void* dst; + unsigned size; + unsigned offset; + bool address; +}; + +class ImmediateTask: public Task { + public: + ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, + unsigned promiseOffset, bool address); + + virtual void run(Context* c); + + Promise* promise; + Promise* offset; + unsigned size; + unsigned promiseOffset; + bool address; +}; + +void +appendImmediateTask(Context* c, Promise* promise, Promise* offset, + unsigned size, unsigned promiseOffset, bool address); + +class ConstantPoolEntry: public Promise { + public: + ConstantPoolEntry(Context* c, Promise* constant); + + virtual int64_t value(); + + virtual bool resolved(); + + Context* c; + Promise* constant; + ConstantPoolEntry* next; + void* address; +}; + +ConstantPoolEntry* appendConstantPoolEntry(Context* c, Promise* constant); + +inline int ha16(int32_t i) { + return ((i >> 16) + ((i & 0x8000) ? 1 : 0)) & 0xffff; +} + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_FIXUP_H diff --git a/src/codegen/target/powerpc/multimethod.cpp b/src/codegen/target/powerpc/multimethod.cpp new file mode 100644 index 0000000000..a214c950be --- /dev/null +++ b/src/codegen/target/powerpc/multimethod.cpp @@ -0,0 +1,139 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" +#include "avian/common.h" +#include "operations.h" + +#include "multimethod.h" +#include "../multimethod.h" + +namespace avian { +namespace codegen { +namespace powerpc { + +using namespace util; + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2) +{ + return operation + + (lir::BinaryOperationCount * operand1) + + (lir::BinaryOperationCount * lir::OperandTypeCount * operand2); +} + +unsigned index(ArchitectureContext* c UNUSED, + lir::TernaryOperation operation, + lir::OperandType operand1) +{ + assert(c, not isBranch(operation)); + + return operation + (lir::NonBranchTernaryOperationCount * operand1); +} + +unsigned branchIndex(ArchitectureContext* c UNUSED, lir::OperandType operand1, + lir::OperandType operand2) +{ + return operand1 + (lir::OperandTypeCount * operand2); +} + +void populateTables(ArchitectureContext* c) { + const lir::OperandType C = lir::ConstantOperand; + const lir::OperandType A = lir::AddressOperand; + const lir::OperandType R = lir::RegisterOperand; + const lir::OperandType M = lir::MemoryOperand; + + OperationType* zo = c->operations; + UnaryOperationType* uo = c->unaryOperations; + BinaryOperationType* bo = c->binaryOperations; + TernaryOperationType* to = c->ternaryOperations; + BranchOperationType* bro = c->branchOperations; + + zo[lir::Return] = return_; + zo[lir::LoadBarrier] = memoryBarrier; + zo[lir::StoreStoreBarrier] = memoryBarrier; + zo[lir::StoreLoadBarrier] = memoryBarrier; + zo[lir::Trap] = trap; + + uo[Multimethod::index(lir::LongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::AlignedLongCall, C)] = CAST1(alignedLongCallC); + + uo[Multimethod::index(lir::LongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::AlignedLongJump, C)] = CAST1(alignedLongJumpC); + + uo[Multimethod::index(lir::Jump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::Jump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::AlignedJump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::AlignedJump, C)] = CAST1(jumpC); + + uo[Multimethod::index(lir::Call, C)] = CAST1(callC); + uo[Multimethod::index(lir::Call, R)] = CAST1(callR); + + uo[Multimethod::index(lir::AlignedCall, C)] = CAST1(callC); + uo[Multimethod::index(lir::AlignedCall, R)] = CAST1(callR); + + bo[index(c, lir::Move, R, R)] = CAST2(moveRR); + bo[index(c, lir::Move, C, R)] = CAST2(moveCR); + bo[index(c, lir::Move, C, M)] = CAST2(moveCM); + bo[index(c, lir::Move, M, R)] = CAST2(moveMR); + bo[index(c, lir::Move, R, M)] = CAST2(moveRM); + bo[index(c, lir::Move, A, R)] = CAST2(moveAR); + + bo[index(c, lir::MoveZ, R, R)] = CAST2(moveZRR); + bo[index(c, lir::MoveZ, M, R)] = CAST2(moveZMR); + bo[index(c, lir::MoveZ, C, R)] = CAST2(moveCR); + + bo[index(c, lir::Negate, R, R)] = CAST2(negateRR); + + to[index(c, lir::Add, R)] = CAST3(addR); + to[index(c, lir::Add, C)] = CAST3(addC); + + to[index(c, lir::Subtract, R)] = CAST3(subR); + to[index(c, lir::Subtract, C)] = CAST3(subC); + + to[index(c, lir::Multiply, R)] = CAST3(multiplyR); + + to[index(c, lir::Divide, R)] = CAST3(divideR); + + to[index(c, lir::Remainder, R)] = CAST3(remainderR); + + to[index(c, lir::ShiftLeft, R)] = CAST3(shiftLeftR); + to[index(c, lir::ShiftLeft, C)] = CAST3(shiftLeftC); + + to[index(c, lir::ShiftRight, R)] = CAST3(shiftRightR); + to[index(c, lir::ShiftRight, C)] = CAST3(shiftRightC); + + to[index(c, lir::UnsignedShiftRight, R)] = CAST3(unsignedShiftRightR); + to[index(c, lir::UnsignedShiftRight, C)] = CAST3(unsignedShiftRightC); + + to[index(c, lir::And, C)] = CAST3(andC); + to[index(c, lir::And, R)] = CAST3(andR); + + to[index(c, lir::Or, C)] = CAST3(orC); + to[index(c, lir::Or, R)] = CAST3(orR); + + to[index(c, lir::Xor, C)] = CAST3(xorC); + to[index(c, lir::Xor, R)] = CAST3(xorR); + + bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR); + bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR); + bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM); + bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM); +} + +} // namespace powerpc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/multimethod.h b/src/codegen/target/powerpc/multimethod.h new file mode 100644 index 0000000000..b3565d0ed9 --- /dev/null +++ b/src/codegen/target/powerpc/multimethod.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_MULTIMETHOD_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_MULTIMETHOD_H + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST3(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +namespace avian { +namespace codegen { +namespace powerpc { + +unsigned index(ArchitectureContext*, + lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2); + +unsigned index(ArchitectureContext* c UNUSED, + lir::TernaryOperation operation, + lir::OperandType operand1); + +unsigned branchIndex(ArchitectureContext* c UNUSED, lir::OperandType operand1, + lir::OperandType operand2); + +void populateTables(ArchitectureContext* c); + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_MULTIMETHOD_H diff --git a/src/codegen/target/powerpc/operations.cpp b/src/codegen/target/powerpc/operations.cpp new file mode 100644 index 0000000000..c82710b41f --- /dev/null +++ b/src/codegen/target/powerpc/operations.cpp @@ -0,0 +1,1097 @@ +/* Copyright (c) 2008-2012, 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 "context.h" +#include "block.h" +#include "avian/common.h" +#include "encode.h" +#include "operations.h" +#include "fixup.h" +#include "multimethod.h" + +using namespace vm; + +namespace avian { +namespace codegen { +namespace powerpc { + +using namespace isa; +using namespace util; + +const int64_t MASK_LO32 = 0x0ffffffff; +const int MASK_LO16 = 0x0ffff; +const int MASK_LO8 = 0x0ff; +// inline int lo32(int64_t i) { return (int)(i & MASK_LO32); } +// inline int hi32(int64_t i) { return lo32(i >> 32); } +inline int lo16(int64_t i) { return (int)(i & MASK_LO16); } +inline int hi16(int64_t i) { return lo16(i >> 16); } +// inline int lo8(int64_t i) { return (int)(i & MASK_LO8); } +// inline int hi8(int64_t i) { return lo8(i >> 8); } + +inline int carry16(target_intptr_t v) { + return static_cast(v) < 0 ? 1 : 0; +} + +void andC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void shiftLeftR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if(size == 8) { + lir::Register Tmp(newTemp(con), newTemp(con)); lir::Register* tmp = &Tmp; + emit(con, subfic(tmp->high, a->low, 32)); + emit(con, slw(t->high, b->high, a->low)); + emit(con, srw(tmp->low, b->low, tmp->high)); + emit(con, or_(t->high, t->high, tmp->low)); + emit(con, addi(tmp->high, a->low, -32)); + emit(con, slw(tmp->low, b->low, tmp->high)); + emit(con, or_(t->high, t->high, tmp->low)); + emit(con, slw(t->low, b->low, a->low)); + freeTemp(con, tmp->high); freeTemp(con, tmp->low); + } else { + emit(con, slw(t->low, b->low, a->low)); + } +} + +void moveRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void shiftLeftC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t) { + int sh = getValue(a); + if (size == 8) { + sh &= 0x3F; + if (sh) { + if (sh < 32) { + emit(con, rlwinm(t->high,b->high,sh,0,31-sh)); + emit(con, rlwimi(t->high,b->low,sh,32-sh,31)); + emit(con, slwi(t->low, b->low, sh)); + } else { + emit(con, rlwinm(t->high,b->low,sh-32,0,63-sh)); + emit(con, li(t->low,0)); + } + } else { + moveRR(con, size, b, size, t); + } + } else { + emit(con, slwi(t->low, b->low, sh & 0x1F)); + } +} + +void shiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if(size == 8) { + lir::Register Tmp(newTemp(con), newTemp(con)); lir::Register* tmp = &Tmp; + emit(con, subfic(tmp->high, a->low, 32)); + emit(con, srw(t->low, b->low, a->low)); + emit(con, slw(tmp->low, b->high, tmp->high)); + emit(con, or_(t->low, t->low, tmp->low)); + emit(con, addic(tmp->high, a->low, -32)); + emit(con, sraw(tmp->low, b->high, tmp->high)); + emit(con, ble(8)); + emit(con, ori(t->low, tmp->low, 0)); + emit(con, sraw(t->high, b->high, a->low)); + freeTemp(con, tmp->high); freeTemp(con, tmp->low); + } else { + emit(con, sraw(t->low, b->low, a->low)); + } +} + +void shiftRightC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t) { + int sh = getValue(a); + if(size == 8) { + sh &= 0x3F; + if (sh) { + if (sh < 32) { + emit(con, rlwinm(t->low,b->low,32-sh,sh,31)); + emit(con, rlwimi(t->low,b->high,32-sh,0,sh-1)); + emit(con, srawi(t->high,b->high,sh)); + } else { + emit(con, srawi(t->high,b->high,31)); + emit(con, srawi(t->low,b->high,sh-32)); + } + } else { + moveRR(con, size, b, size, t); + } + } else { + emit(con, srawi(t->low, b->low, sh & 0x1F)); + } +} + +void unsignedShiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + emit(con, srw(t->low, b->low, a->low)); + if(size == 8) { + lir::Register Tmp(newTemp(con), newTemp(con)); lir::Register* tmp = &Tmp; + emit(con, subfic(tmp->high, a->low, 32)); + emit(con, slw(tmp->low, b->high, tmp->high)); + emit(con, or_(t->low, t->low, tmp->low)); + emit(con, addi(tmp->high, a->low, -32)); + emit(con, srw(tmp->low, b->high, tmp->high)); + emit(con, or_(t->low, t->low, tmp->low)); + emit(con, srw(t->high, b->high, a->low)); + freeTemp(con, tmp->high); freeTemp(con, tmp->low); + } +} + +void unsignedShiftRightC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t) { + int sh = getValue(a); + if (size == 8) { + if (sh & 0x3F) { + if (sh == 32) { + lir::Register high(b->high); + moveRR(con, 4, &high, 4, t); + emit(con, li(t->high,0)); + } else if (sh < 32) { + emit(con, srwi(t->low, b->low, sh)); + emit(con, rlwimi(t->low,b->high,32-sh,0,sh-1)); + emit(con, rlwinm(t->high,b->high,32-sh,sh,31)); + } else { + emit(con, rlwinm(t->low,b->high,64-sh,sh-32,31)); + emit(con, li(t->high,0)); + } + } else { + moveRR(con, size, b, size, t); + } + } else { + if (sh & 0x1F) { + emit(con, srwi(t->low, b->low, sh & 0x1F)); + } else { + moveRR(con, size, b, size, t); + } + } +} + +void jumpR(Context* c, unsigned size UNUSED, lir::Register* target) { + assert(c, size == TargetBytesPerWord); + + emit(c, mtctr(target->low)); + emit(c, bctr()); +} + +void swapRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) { + assert(c, aSize == TargetBytesPerWord); + assert(c, bSize == TargetBytesPerWord); + + lir::Register tmp(c->client->acquireTemporary()); + moveRR(c, aSize, a, bSize, &tmp); + moveRR(c, bSize, b, aSize, a); + moveRR(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); +} + +void moveRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst) { + switch (srcSize) { + case 1: + emit(c, extsb(dst->low, src->low)); + break; + + case 2: + emit(c, extsh(dst->low, src->low)); + break; + + case 4: + case 8: + if (srcSize == 4 and dstSize == 8) { + moveRR(c, 4, src, 4, dst); + emit(c, srawi(dst->high, src->low, 31)); + } else if (srcSize == 8 and dstSize == 8) { + lir::Register srcHigh(src->high); + lir::Register dstHigh(dst->high); + + if (src->high == dst->low) { + if (src->low == dst->high) { + swapRR(c, 4, src, 4, dst); + } else { + moveRR(c, 4, &srcHigh, 4, &dstHigh); + moveRR(c, 4, src, 4, dst); + } + } else { + moveRR(c, 4, src, 4, dst); + moveRR(c, 4, &srcHigh, 4, &dstHigh); + } + } else if (src->low != dst->low) { + emit(c, mr(dst->low, src->low)); + } + break; + + default: abort(c); + } +} + +void moveZRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned, lir::Register* dst) { + switch (srcSize) { + case 2: + emit(c, andi(dst->low, src->low, 0xFFFF)); + break; + + default: abort(c); + } +} + +void moveCR2(Context* c, unsigned, lir::Constant* src, + unsigned dstSize, lir::Register* dst, unsigned promiseOffset) { + if (dstSize <= 4) { + if (src->value->resolved()) { + int32_t v = src->value->value(); + if (fitsInInt16(v)) { + emit(c, li(dst->low, v)); + } else { + emit(c, lis(dst->low, v >> 16)); + emit(c, ori(dst->low, dst->low, v)); + } + } else { + appendImmediateTask + (c, src->value, offsetPromise(c), TargetBytesPerWord, promiseOffset, false); + emit(c, lis(dst->low, 0)); + emit(c, ori(dst->low, dst->low, 0)); + } + } else { + abort(c); // todo + } +} + +void moveCR(Context* c, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Register* dst) { + moveCR2(c, srcSize, src, dstSize, dst, 0); +} + +void addR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if(size == 8) { + emit(con, addc(t->low, a->low, b->low)); + emit(con, adde(t->high, a->high, b->high)); + } else { + emit(con, add(t->low, a->low, b->low)); + } +} + +void addC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t) { + assert(con, size == TargetBytesPerWord); + + int32_t i = getValue(a); + if(i) { + emit(con, addi(t->low, b->low, lo16(i))); + if(not fitsInInt16(i)) + emit(con, addis(t->low, t->low, hi16(i) + carry16(i))); + } else { + moveRR(con, size, b, size, t); + } +} + +void subR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if(size == 8) { + emit(con, subfc(t->low, a->low, b->low)); + emit(con, subfe(t->high, a->high, b->high)); + } else { + emit(con, subf(t->low, a->low, b->low)); + } +} + +void subC(Context* c, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t) { + assert(c, size == TargetBytesPerWord); + + ResolvedPromise promise(- a->value->value()); + lir::Constant constant(&promise); + addC(c, size, &constant, b, t); +} + +void multiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + if(size == 8) { + bool useTemporaries = b->low == t->low; + int tmpLow; + int tmpHigh; + if (useTemporaries) { + tmpLow = con->client->acquireTemporary(); + tmpHigh = con->client->acquireTemporary(); + } else { + tmpLow = t->low; + tmpHigh = t->high; + } + + emit(con, mullw(tmpHigh, a->high, b->low)); + emit(con, mullw(tmpLow, a->low, b->high)); + emit(con, add(t->high, tmpHigh, tmpLow)); + emit(con, mulhwu(tmpLow, a->low, b->low)); + emit(con, add(t->high, t->high, tmpLow)); + emit(con, mullw(t->low, a->low, b->low)); + + if (useTemporaries) { + con->client->releaseTemporary(tmpLow); + con->client->releaseTemporary(tmpHigh); + } + } else { + emit(con, mullw(t->low, a->low, b->low)); + } +} + +void divideR(Context* con, unsigned size UNUSED, lir::Register* a, lir::Register* b, lir::Register* t) { + assert(con, size == 4); + emit(con, divw(t->low, b->low, a->low)); +} + +void remainderR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t) { + bool useTemporary = b->low == t->low; + lir::Register tmp(t->low); + if (useTemporary) { + tmp.low = con->client->acquireTemporary(); + } + + divideR(con, size, a, b, &tmp); + multiplyR(con, size, a, &tmp, &tmp); + subR(con, size, &tmp, b, t); + + if (useTemporary) { + con->client->releaseTemporary(tmp.low); + } +} + +int +normalize(Context* c, int offset, int index, unsigned scale, + bool* preserveIndex, bool* release) { + if (offset != 0 or scale != 1) { + lir::Register normalizedIndex + (*preserveIndex ? c->client->acquireTemporary() : index); + + if (*preserveIndex) { + *release = true; + *preserveIndex = false; + } else { + *release = false; + } + + int scaled; + + if (scale != 1) { + lir::Register unscaledIndex(index); + + ResolvedPromise scalePromise(log(scale)); + lir::Constant scaleConstant(&scalePromise); + + shiftLeftC(c, TargetBytesPerWord, &scaleConstant, + &unscaledIndex, &normalizedIndex); + + scaled = normalizedIndex.low; + } else { + scaled = index; + } + + if (offset != 0) { + lir::Register untranslatedIndex(scaled); + + ResolvedPromise offsetPromise(offset); + lir::Constant offsetConstant(&offsetPromise); + + addC(c, TargetBytesPerWord, &offsetConstant, + &untranslatedIndex, &normalizedIndex); + } + + return normalizedIndex.low; + } else { + *release = false; + return index; + } +} + +void store(Context* c, unsigned size, lir::Register* src, + int base, int offset, int index, unsigned scale, bool preserveIndex) { + if (index != lir::NoRegister) { + bool release; + int normalized = normalize + (c, offset, index, scale, &preserveIndex, &release); + + switch (size) { + case 1: + emit(c, stbx(src->low, base, normalized)); + break; + + case 2: + emit(c, sthx(src->low, base, normalized)); + break; + + case 4: + emit(c, stwx(src->low, base, normalized)); + break; + + case 8: { + lir::Register srcHigh(src->high); + store(c, 4, &srcHigh, base, 0, normalized, 1, preserveIndex); + store(c, 4, src, base, 4, normalized, 1, preserveIndex); + } break; + + default: abort(c); + } + + if (release) c->client->releaseTemporary(normalized); + } else { + switch (size) { + case 1: + emit(c, stb(src->low, base, offset)); + break; + + case 2: + emit(c, sth(src->low, base, offset)); + break; + + case 4: + emit(c, stw(src->low, base, offset)); + break; + + case 8: { + lir::Register srcHigh(src->high); + store(c, 4, &srcHigh, base, offset, lir::NoRegister, 1, false); + store(c, 4, src, base, offset + 4, lir::NoRegister, 1, false); + } break; + + default: abort(c); + } + } +} + +void moveRM(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst) { + assert(c, srcSize == dstSize); + + store(c, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); +} + +void moveAndUpdateRM(Context* c, unsigned srcSize UNUSED, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst) { + assert(c, srcSize == TargetBytesPerWord); + assert(c, dstSize == TargetBytesPerWord); + + if (dst->index == lir::NoRegister) { + emit(c, stwu(src->low, dst->base, dst->offset)); + } else { + assert(c, dst->offset == 0); + assert(c, dst->scale == 1); + + emit(c, stwux(src->low, dst->base, dst->index)); + } +} + +void load(Context* c, unsigned srcSize, int base, int offset, int index, + unsigned scale, unsigned dstSize, lir::Register* dst, + bool preserveIndex, bool signExtend) { + if (index != lir::NoRegister) { + bool release; + int normalized = normalize + (c, offset, index, scale, &preserveIndex, &release); + + switch (srcSize) { + case 1: + emit(c, lbzx(dst->low, base, normalized)); + if (signExtend) { + emit(c, extsb(dst->low, dst->low)); + } + break; + + case 2: + if (signExtend) { + emit(c, lhax(dst->low, base, normalized)); + } else { + emit(c, lhzx(dst->low, base, normalized)); + } + break; + + case 4: + case 8: { + if (srcSize == 4 and dstSize == 8) { + load(c, 4, base, 0, normalized, 1, 4, dst, preserveIndex, false); + moveRR(c, 4, dst, 8, dst); + } else if (srcSize == 8 and dstSize == 8) { + lir::Register dstHigh(dst->high); + load(c, 4, base, 0, normalized, 1, 4, &dstHigh, preserveIndex, false); + load(c, 4, base, 4, normalized, 1, 4, dst, preserveIndex, false); + } else { + emit(c, lwzx(dst->low, base, normalized)); + } + } break; + + default: abort(c); + } + + if (release) c->client->releaseTemporary(normalized); + } else { + switch (srcSize) { + case 1: + emit(c, lbz(dst->low, base, offset)); + if (signExtend) { + emit(c, extsb(dst->low, dst->low)); + } + break; + + case 2: + if (signExtend) { + emit(c, lha(dst->low, base, offset)); + } else { + emit(c, lha(dst->low, base, offset)); + } + break; + + case 4: + emit(c, lwz(dst->low, base, offset)); + break; + + case 8: { + if (dstSize == 8) { + lir::Register dstHigh(dst->high); + load(c, 4, base, offset, lir::NoRegister, 1, 4, &dstHigh, false, false); + load(c, 4, base, offset + 4, lir::NoRegister, 1, 4, dst, false, false); + } else { + emit(c, lwzx(dst->low, base, offset)); + } + } break; + + default: abort(c); + } + } +} + +void moveMR(Context* c, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst) { + load(c, srcSize, src->base, src->offset, src->index, src->scale, + dstSize, dst, true, true); +} + +void moveZMR(Context* c, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst) { + load(c, srcSize, src->base, src->offset, src->index, src->scale, + dstSize, dst, true, false); +} + +void andR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) { + if (size == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + lir::Register dh(dst->high); + + andR(c, 4, a, b, dst); + andR(c, 4, &ah, &bh, &dh); + } else { + emit(c, and_(dst->low, a->low, b->low)); + } +} + +void andC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) { + int64_t v = a->value->value(); + + if (size == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + lir::Register dh(dst->high); + + andC(c, 4, &al, b, dst); + andC(c, 4, &ah, &bh, &dh); + } else { + // bitmasks of the form regex 0*1*0* can be handled in a single + // rlwinm instruction, hence the following: + + uint32_t v32 = static_cast(v); + unsigned state = 0; + unsigned start = 0; + unsigned end = 31; + for (unsigned i = 0; i < 32; ++i) { + unsigned bit = (v32 >> i) & 1; + switch (state) { + case 0: + if (bit) { + start = i; + state = 1; + } + break; + + case 1: + if (bit == 0) { + end = i - 1; + state = 2; + } + break; + + case 2: + if (bit) { + // not in 0*1*0* form. We can only use andi(s) if either + // the topmost or bottommost 16 bits are zero. + + if ((v32 >> 16) == 0) { + emit(c, andi(dst->low, b->low, v32)); + } else if ((v32 & 0xFFFF) == 0) { + emit(c, andis(dst->low, b->low, v32 >> 16)); + } else { + bool useTemporary = b->low == dst->low; + lir::Register tmp(dst->low); + if (useTemporary) { + tmp.low = c->client->acquireTemporary(); + } + + moveCR(c, 4, a, 4, &tmp); + andR(c, 4, b, &tmp, dst); + + if (useTemporary) { + c->client->releaseTemporary(tmp.low); + } + } + return; + } + break; + } + } + + if (state) { + if (start != 0 or end != 31) { + emit(c, rlwinm(dst->low, b->low, 0, 31 - end, 31 - start)); + } else { + moveRR(c, 4, b, 4, dst); + } + } else { + emit(c, li(dst->low, 0)); + } + } +} + +void orR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) { + if (size == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + lir::Register dh(dst->high); + + orR(c, 4, a, b, dst); + orR(c, 4, &ah, &bh, &dh); + } else { + emit(c, or_(dst->low, a->low, b->low)); + } +} + +void orC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) { + int64_t v = a->value->value(); + + if (size == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + lir::Register dh(dst->high); + + orC(c, 4, &al, b, dst); + orC(c, 4, &ah, &bh, &dh); + } else { + emit(c, ori(b->low, dst->low, v)); + if (v >> 16) { + emit(c, oris(dst->low, dst->low, v >> 16)); + } + } +} + +void xorR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst) { + if (size == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + lir::Register dh(dst->high); + + xorR(c, 4, a, b, dst); + xorR(c, 4, &ah, &bh, &dh); + } else { + emit(c, xor_(dst->low, a->low, b->low)); + } +} + +void xorC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst) { + uint64_t v = a->value->value(); + + if (size == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + lir::Register dh(dst->high); + + xorC(c, 4, &al, b, dst); + xorC(c, 4, &ah, &bh, &dh); + } else { + if (v >> 16) { + emit(c, xoris(b->low, dst->low, v >> 16)); + emit(c, xori(dst->low, dst->low, v)); + } else { + emit(c, xori(b->low, dst->low, v)); + } + } +} + +void moveAR2(Context* c, unsigned srcSize UNUSED, lir::Address* src, + unsigned dstSize, lir::Register* dst, unsigned promiseOffset) { + assert(c, srcSize == 4 and dstSize == 4); + + lir::Memory memory(dst->low, 0, -1, 0); + + appendImmediateTask + (c, src->address, offsetPromise(c), TargetBytesPerWord, promiseOffset, true); + + emit(c, lis(dst->low, 0)); + moveMR(c, dstSize, &memory, dstSize, dst); +} + +void moveAR(Context* c, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst) { + moveAR2(c, srcSize, src, dstSize, dst, 0); +} + +void compareRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) { + assert(c, aSize == 4 and bSize == 4); + + emit(c, cmpw(b->low, a->low)); +} + +void compareCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) { + assert(c, aSize == 4 and bSize == 4); + + if (a->value->resolved() and fitsInInt16(a->value->value())) { + emit(c, cmpwi(b->low, a->value->value())); + } else { + lir::Register tmp(c->client->acquireTemporary()); + moveCR(c, aSize, a, bSize, &tmp); + compareRR(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +void compareCM(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b) { + assert(c, aSize == 4 and bSize == 4); + + lir::Register tmp(c->client->acquireTemporary()); + moveMR(c, bSize, b, bSize, &tmp); + compareCR(c, aSize, a, bSize, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void compareRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Memory* b) { + assert(c, aSize == 4 and bSize == 4); + + lir::Register tmp(c->client->acquireTemporary()); + moveMR(c, bSize, b, bSize, &tmp); + compareRR(c, aSize, a, bSize, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void compareUnsignedRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) { + assert(c, aSize == 4 and bSize == 4); + + emit(c, cmplw(b->low, a->low)); +} + +void compareUnsignedCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) { + assert(c, aSize == 4 and bSize == 4); + + if (a->value->resolved() and (a->value->value() >> 16) == 0) { + emit(c, cmplwi(b->low, a->value->value())); + } else { + lir::Register tmp(c->client->acquireTemporary()); + moveCR(c, aSize, a, bSize, &tmp); + compareUnsignedRR(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +int32_t +branch(Context* c, lir::TernaryOperation op) { + switch (op) { + case lir::JumpIfEqual: + return beq(0); + + case lir::JumpIfNotEqual: + return bne(0); + + case lir::JumpIfLess: + return blt(0); + + case lir::JumpIfGreater: + return bgt(0); + + case lir::JumpIfLessOrEqual: + return ble(0); + + case lir::JumpIfGreaterOrEqual: + return bge(0); + + default: + abort(c); + } +} + +void conditional(Context* c, int32_t branch, lir::Constant* target) { + appendOffsetTask(c, target->value, offsetPromise(c), true); + emit(c, branch); +} + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target) { + conditional(c, branch(c, op), target); +} + +void branchLong(Context* c, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned) { + compareSigned(c, 4, ah, 4, bh); + + unsigned next = 0; + + switch (op) { + case lir::JumpIfEqual: + next = c->code.length(); + emit(c, bne(0)); + + compareSigned(c, 4, al, 4, bl); + conditional(c, beq(0), target); + break; + + case lir::JumpIfNotEqual: + conditional(c, bne(0), target); + + compareSigned(c, 4, al, 4, bl); + conditional(c, bne(0), target); + break; + + case lir::JumpIfLess: + conditional(c, blt(0), target); + + next = c->code.length(); + emit(c, bgt(0)); + + compareUnsigned(c, 4, al, 4, bl); + conditional(c, blt(0), target); + break; + + case lir::JumpIfGreater: + conditional(c, bgt(0), target); + + next = c->code.length(); + emit(c, blt(0)); + + compareUnsigned(c, 4, al, 4, bl); + conditional(c, bgt(0), target); + break; + + case lir::JumpIfLessOrEqual: + conditional(c, blt(0), target); + + next = c->code.length(); + emit(c, bgt(0)); + + compareUnsigned(c, 4, al, 4, bl); + conditional(c, ble(0), target); + break; + + case lir::JumpIfGreaterOrEqual: + conditional(c, bgt(0), target); + + next = c->code.length(); + emit(c, blt(0)); + + compareUnsigned(c, 4, al, 4, bl); + conditional(c, bge(0), target); + break; + + default: + abort(c); + } + + if (next) { + updateOffset + (c->s, c->code.data + next, true, reinterpret_cast + (c->code.data + c->code.length()), 0); + } +} + +void branchRR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target) { + if (size > TargetBytesPerWord) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR), + CAST2(compareUnsignedRR)); + } else { + compareRR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchCR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target) { + if (size > TargetBytesPerWord) { + int64_t v = a->value->value(); + + ResolvedPromise low(v & ~static_cast(0)); + lir::Constant al(&low); + + ResolvedPromise high((v >> 32) & ~static_cast(0)); + lir::Constant ah(&high); + + lir::Register bh(b->high); + + branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR), + CAST2(compareUnsignedCR)); + } else { + compareCR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchRM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target) { + assert(c, size <= TargetBytesPerWord); + + compareRM(c, size, a, size, b); + branch(c, op, target); +} + +void branchCM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target) { + assert(c, size <= TargetBytesPerWord); + + compareCM(c, size, a, size, b); + branch(c, op, target); +} + +void moveCM(Context* c, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Memory* dst) { + switch (dstSize) { + case 8: { + lir::Constant srcHigh + (shiftMaskPromise(c, src->value, 32, 0xFFFFFFFF)); + lir::Constant srcLow + (shiftMaskPromise(c, src->value, 0, 0xFFFFFFFF)); + + lir::Memory dstLow + (dst->base, dst->offset + 4, dst->index, dst->scale); + + moveCM(c, 4, &srcLow, 4, &dstLow); + moveCM(c, 4, &srcHigh, 4, dst); + } break; + + default: + lir::Register tmp(c->client->acquireTemporary()); + moveCR(c, srcSize, src, dstSize, &tmp); + moveRM(c, dstSize, &tmp, dstSize, dst); + c->client->releaseTemporary(tmp.low); + } +} + +void negateRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Register* dst) { + assert(c, srcSize == dstSize); + + if (srcSize == 8) { + lir::Register dstHigh(dst->high); + + emit(c, subfic(dst->low, src->low, 0)); + emit(c, subfze(dst->high, src->high)); + } else { + emit(c, neg(dst->low, src->low)); + } +} + +void callR(Context* c, unsigned size UNUSED, lir::Register* target) { + assert(c, size == TargetBytesPerWord); + + emit(c, mtctr(target->low)); + emit(c, bctrl()); +} + +void callC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + appendOffsetTask(c, target->value, offsetPromise(c), false); + emit(c, bl(0)); +} + +void longCallC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + lir::Register tmp(0); + moveCR2(c, TargetBytesPerWord, target, TargetBytesPerWord, &tmp, 12); + callR(c, TargetBytesPerWord, &tmp); +} + +void alignedLongCallC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + lir::Register tmp(c->client->acquireTemporary()); + lir::Address address(appendConstantPoolEntry(c, target->value)); + moveAR2(c, TargetBytesPerWord, &address, TargetBytesPerWord, &tmp, 12); + callR(c, TargetBytesPerWord, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void longJumpC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + lir::Register tmp(0); + moveCR2(c, TargetBytesPerWord, target, TargetBytesPerWord, &tmp, 12); + jumpR(c, TargetBytesPerWord, &tmp); +} + +void alignedLongJumpC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + lir::Register tmp(c->client->acquireTemporary()); + lir::Address address(appendConstantPoolEntry(c, target->value)); + moveAR2(c, TargetBytesPerWord, &address, TargetBytesPerWord, &tmp, 12); + jumpR(c, TargetBytesPerWord, &tmp); + c->client->releaseTemporary(tmp.low); +} + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* target) { + assert(c, size == TargetBytesPerWord); + + appendOffsetTask(c, target->value, offsetPromise(c), false); + emit(c, b(0)); +} + +void return_(Context* c) { + emit(c, blr()); +} + +void trap(Context* c) { + emit(c, isa::trap()); +} + +void memoryBarrier(Context* c) { + emit(c, sync(0)); +} + +} // namespace powerpc +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/powerpc/operations.h b/src/codegen/target/powerpc/operations.h new file mode 100644 index 0000000000..3e16dc5292 --- /dev/null +++ b/src/codegen/target/powerpc/operations.h @@ -0,0 +1,197 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_OPERATIONS_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_OPERATIONS_H + +#include "context.h" + +namespace avian { +namespace codegen { +namespace powerpc { + +inline void emit(Context* con, int code) { con->code.append4(vm::targetV4(code)); } +inline int newTemp(Context* con) { return con->client->acquireTemporary(); } +inline void freeTemp(Context* con, int r) { con->client->releaseTemporary(r); } +inline int64_t getValue(lir::Constant* c) { return c->value->value(); } + +void andC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void shiftLeftR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void moveRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void shiftLeftC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t); + +void shiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void shiftRightC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t); + +void unsignedShiftRightR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void unsignedShiftRightC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t); + +void jumpR(Context* c, unsigned size UNUSED, lir::Register* target); + +void swapRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void moveRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize, lir::Register* dst); + +void moveZRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned, lir::Register* dst); + +void moveCR2(Context* c, unsigned, lir::Constant* src, + unsigned dstSize, lir::Register* dst, unsigned promiseOffset); + +void moveCR(Context* c, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Register* dst); + +void addR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void addC(Context* con, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t); + +void subR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void subC(Context* c, unsigned size, lir::Constant* a, lir::Register* b, lir::Register* t); + +void multiplyR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +void divideR(Context* con, unsigned size UNUSED, lir::Register* a, lir::Register* b, lir::Register* t); + +void remainderR(Context* con, unsigned size, lir::Register* a, lir::Register* b, lir::Register* t); + +int +normalize(Context* c, int offset, int index, unsigned scale, + bool* preserveIndex, bool* release); + +void store(Context* c, unsigned size, lir::Register* src, + int base, int offset, int index, unsigned scale, bool preserveIndex); + +void moveRM(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst); + +void moveAndUpdateRM(Context* c, unsigned srcSize UNUSED, lir::Register* src, + unsigned dstSize UNUSED, lir::Memory* dst); + +void load(Context* c, unsigned srcSize, int base, int offset, int index, + unsigned scale, unsigned dstSize, lir::Register* dst, + bool preserveIndex, bool signExtend); + +void moveMR(Context* c, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst); + +void moveZMR(Context* c, unsigned srcSize, lir::Memory* src, + unsigned dstSize, lir::Register* dst); + +void andR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void andC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void orR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void orC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void xorR(Context* c, unsigned size, lir::Register* a, + lir::Register* b, lir::Register* dst); + +void xorC(Context* c, unsigned size, lir::Constant* a, + lir::Register* b, lir::Register* dst); + +void moveAR2(Context* c, unsigned srcSize UNUSED, lir::Address* src, + unsigned dstSize, lir::Register* dst, unsigned promiseOffset); + +void moveAR(Context* c, unsigned srcSize, lir::Address* src, + unsigned dstSize, lir::Register* dst); + +void compareRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void compareCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void compareCM(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b); + +void compareRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Memory* b); + +void compareUnsignedRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void compareUnsignedCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +int32_t branch(Context* c, lir::TernaryOperation op); + +void conditional(Context* c, int32_t branch, lir::Constant* target); + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target); + +void branchLong(Context* c, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compareSigned, + BinaryOperationType compareUnsigned); + +void branchRR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target); + +void branchCR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target); + +void branchRM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target); + +void branchCM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target); + +void moveCM(Context* c, unsigned srcSize, lir::Constant* src, + unsigned dstSize, lir::Memory* dst); + +void negateRR(Context* c, unsigned srcSize, lir::Register* src, + unsigned dstSize UNUSED, lir::Register* dst); + +void callR(Context* c, unsigned size UNUSED, lir::Register* target); + +void callC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void longCallC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void alignedLongCallC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void longJumpC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void alignedLongJumpC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* target); + +void return_(Context* c); + +void trap(Context* c); + +void memoryBarrier(Context* c); + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_OPERATIONS_H diff --git a/src/codegen/target/powerpc/registers.h b/src/codegen/target/powerpc/registers.h new file mode 100644 index 0000000000..ce395a373e --- /dev/null +++ b/src/codegen/target/powerpc/registers.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_POWERPC_REGISTERS_H +#define AVIAN_CODEGEN_ASSEMBLER_POWERPC_REGISTERS_H + +namespace avian { +namespace codegen { +namespace powerpc { + + +} // namespace powerpc +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_POWERPC_REGISTERS_H diff --git a/src/codegen/target/x86/assembler.cpp b/src/codegen/target/x86/assembler.cpp new file mode 100644 index 0000000000..e580f197a2 --- /dev/null +++ b/src/codegen/target/x86/assembler.cpp @@ -0,0 +1,1159 @@ +/* Copyright (c) 2008-2012, 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 +#include + +#include "avian/environment.h" +#include "avian/target.h" +#include "avian/alloc-vector.h" +#include "avian/common.h" +#include "avian/allocator.h" +#include "avian/zone.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "context.h" +#include "block.h" +#include "fixup.h" +#include "padding.h" +#include "registers.h" +#include "operations.h" +#include "detect.h" +#include "multimethod.h" +#include "../multimethod.h" + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +using namespace vm; +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace x86 { + +const unsigned FrameHeaderSize = (UseFramePointer ? 2 : 1); + +const unsigned StackAlignmentInBytes = 16; +const unsigned StackAlignmentInWords = StackAlignmentInBytes / TargetBytesPerWord; + +unsigned +argumentFootprint(unsigned footprint) +{ + return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); +} + +uint32_t +read4(uint8_t* p) +{ + uint32_t v; memcpy(&v, p, 4); + return v; +} + +void +nextFrame(ArchitectureContext* c UNUSED, uint8_t* start, unsigned size UNUSED, + unsigned footprint, void*, bool mostRecent, + unsigned targetParameterFootprint, void** ip, void** stack) +{ + assert(c, *ip >= start); + assert(c, *ip <= start + size); + + uint8_t* instruction = static_cast(*ip); + + // skip stack overflow check, if present: + if (TargetBytesPerWord == 4) { + if (*start == 0x39) { + start += 12; + } + } else if (*start == 0x48 and start[1] == 0x39) { + start += 13; + } + + if (instruction <= start) { + assert(c, mostRecent); + *ip = static_cast(*stack)[0]; + return; + } + + if (UseFramePointer) { + // skip preamble + start += (TargetBytesPerWord == 4 ? 3 : 4); + + if (instruction <= start or *instruction == 0x5d) { + assert(c, mostRecent); + + *ip = static_cast(*stack)[1]; + *stack = static_cast(*stack) + 1; + return; + } + } + + if (*instruction == 0xc3) { // return + *ip = static_cast(*stack)[0]; + return; + } + + unsigned offset = footprint + FrameHeaderSize - (mostRecent ? 1 : 0); + + if (TailCalls) { + if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { + offset += argumentFootprint(targetParameterFootprint) + - StackAlignmentInWords; + } + + // check for post-non-tail-call stack adjustment of the form "add + // $offset,%rsp": + if (TargetBytesPerWord == 4) { + if ((*instruction == 0x83 or *instruction == 0x81) + and instruction[1] == 0xec) + { + offset + -= (*instruction == 0x83 ? instruction[2] : read4(instruction + 2)) + / TargetBytesPerWord; + } + } else if (*instruction == 0x48 + and (instruction[1] == 0x83 or instruction[1] == 0x81) + and instruction[2] == 0xec) + { + offset + -= (instruction[1] == 0x83 ? instruction[3] : read4(instruction + 3)) + / TargetBytesPerWord; + } + + // todo: check for and handle tail calls + } + + *ip = static_cast(*stack)[offset]; + *stack = static_cast(*stack) + offset; +} + +class MyArchitecture: public Architecture { + public: + MyArchitecture(System* system, bool useNativeFeatures): + c(system, useNativeFeatures), + referenceCount(0), + myRegisterFile(GeneralRegisterMask, useSSE(&c) ? FloatRegisterMask : 0) + { + populateTables(&c); + } + + virtual unsigned floatRegisterSize() { + if (useSSE(&c)) { + return 8; + } else { + return 0; + } + } + + virtual const RegisterFile* registerFile() { + return &myRegisterFile; + } + + virtual int scratch() { + return rax; + } + + virtual int stack() { + return rsp; + } + + virtual int thread() { + return rbx; + } + + virtual int returnLow() { + return rax; + } + + virtual int returnHigh() { + return (TargetBytesPerWord == 4 ? rdx : lir::NoRegister); + } + + virtual int virtualCallTarget() { + return rax; + } + + virtual int virtualCallIndex() { + return rdx; + } + + virtual bool bigEndian() { + return false; + } + + virtual uintptr_t maximumImmediateJump() { + return 0x7FFFFFFF; + } + + virtual bool reserved(int register_) { + switch (register_) { + case rbp: + return UseFramePointer; + + case rsp: + case rbx: + return true; + + default: + return false; + } + } + + virtual unsigned frameFootprint(unsigned footprint) { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + return max(footprint, StackAlignmentInWords); +#else + return max(footprint > argumentRegisterCount() ? + footprint - argumentRegisterCount() : 0, + StackAlignmentInWords); +#endif + } + + virtual unsigned argumentFootprint(unsigned footprint) { + return x86::argumentFootprint(footprint); + } + + virtual bool argumentAlignment() { + return false; + } + + virtual bool argumentRegisterAlignment() { + return false; + } + + virtual unsigned argumentRegisterCount() { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + if (TargetBytesPerWord == 8) return 4; else +#else + if (TargetBytesPerWord == 8) return 6; else +#endif + return 0; + } + + virtual int argumentRegister(unsigned index) { + assert(&c, TargetBytesPerWord == 8); + switch (index) { +#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE + case 0: + return rcx; + case 1: + return rdx; + case 2: + return r8; + case 3: + return r9; +#else + case 0: + return rdi; + case 1: + return rsi; + case 2: + return rdx; + case 3: + return rcx; + case 4: + return r8; + case 5: + return r9; +#endif + default: + abort(&c); + } + } + + virtual bool hasLinkRegister() { + return false; + } + + 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(lir::UnaryOperation op, void* returnAddress, + void* newTarget) + { + bool assertAlignment UNUSED; + switch (op) { + case lir::AlignedCall: + op = lir::Call; + assertAlignment = true; + break; + + case lir::AlignedJump: + op = lir::Jump; + assertAlignment = true; + break; + + case lir::AlignedLongCall: + op = lir::LongCall; + assertAlignment = true; + break; + + case lir::AlignedLongJump: + op = lir::LongJump; + assertAlignment = true; + break; + + default: + assertAlignment = false; + } + + if (TargetBytesPerWord == 4 or op == lir::Call or op == lir::Jump) { + uint8_t* instruction = static_cast(returnAddress) - 5; + + assert(&c, ((op == lir::Call or op == lir::LongCall) and *instruction == 0xE8) + or ((op == lir::Jump or op == lir::LongJump) and *instruction == 0xE9)); + + assert(&c, (not assertAlignment) + or reinterpret_cast(instruction + 1) % 4 == 0); + + intptr_t v = static_cast(newTarget) + - static_cast(returnAddress); + + assert(&c, vm::fitsInInt32(v)); + + int32_t v32 = v; + + memcpy(instruction + 1, &v32, 4); + } else { + uint8_t* instruction = static_cast(returnAddress) - 13; + + assert(&c, instruction[0] == 0x49 and instruction[1] == 0xBA); + assert(&c, instruction[10] == 0x41 and instruction[11] == 0xFF); + assert(&c, (op == lir::LongCall and instruction[12] == 0xD2) + or (op == lir::LongJump and instruction[12] == 0xE2)); + + assert(&c, (not assertAlignment) + or reinterpret_cast(instruction + 2) % 8 == 0); + + memcpy(instruction + 2, &newTarget, 8); + } + } + + virtual void setConstant(void* dst, uint64_t constant) { + target_uintptr_t v = targetVW(constant); + memcpy(dst, &v, TargetBytesPerWord); + } + + virtual unsigned alignFrameSize(unsigned sizeInWords) { + return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) + - FrameHeaderSize; + } + + virtual void nextFrame(void* start, unsigned size, unsigned footprint, + void* link, bool mostRecent, + unsigned targetParameterFootprint, void** ip, + void** stack) + { + x86::nextFrame(&c, static_cast(start), size, footprint, + link, mostRecent, targetParameterFootprint, ip, stack); + } + + virtual void* frameIp(void* stack) { + return stack ? *static_cast(stack) : 0; + } + + virtual unsigned frameHeaderSize() { + return FrameHeaderSize; + } + + virtual unsigned frameReturnAddressSize() { + return 1; + } + + virtual unsigned frameFooterSize() { + return 0; + } + + virtual bool alwaysCondensed(lir::BinaryOperation op) { + switch(op) { + case lir::Float2Float: + case lir::Float2Int: + case lir::Int2Float: + case lir::FloatAbsolute: + case lir::FloatNegate: + case lir::FloatSquareRoot: + return false; + + case lir::Negate: + case lir::Absolute: + return true; + + default: + abort(&c); + } + } + + virtual bool alwaysCondensed(lir::TernaryOperation) { + return true; + } + + virtual int returnAddressOffset() { + return 0; + } + + virtual int framePointerOffset() { + return UseFramePointer ? -1 : 0; + } + + virtual void plan + (lir::UnaryOperation, + unsigned, OperandMask& aMask, + bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand) | (1 << lir::ConstantOperand); + *thunk = false; + } + + virtual void planSource + (lir::BinaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned bSize, bool* thunk) + { + aMask.registerMask = GeneralRegisterMask | + (static_cast(GeneralRegisterMask) << 32); + + *thunk = false; + + switch (op) { + case lir::Negate: + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = (static_cast(1) << (rdx + 32)) + | (static_cast(1) << rax); + break; + + case lir::Absolute: + if (aSize <= TargetBytesPerWord) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = (static_cast(1) << rax); + } else { + *thunk = true; + } + break; + + case lir::FloatAbsolute: + if (useSSE(&c)) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + } else { + *thunk = true; + } + break; + + case lir::FloatNegate: + // floatNegateRR does not support doubles + if (useSSE(&c) and aSize == 4 and bSize == 4) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = FloatRegisterMask; + } else { + *thunk = true; + } + break; + + case lir::FloatSquareRoot: + if (useSSE(&c)) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + aMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + } else { + *thunk = true; + } + break; + + case lir::Float2Float: + if (useSSE(&c)) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + aMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + } else { + *thunk = true; + } + break; + + case lir::Float2Int: + // todo: Java requires different semantics than SSE for + // converting floats to integers, we we need to either use + // thunks or produce inline machine code which handles edge + // cases properly. + if (false and useSSE(&c) and bSize <= TargetBytesPerWord) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + aMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + } else { + *thunk = true; + } + break; + + case lir::Int2Float: + if (useSSE(&c) and aSize <= TargetBytesPerWord) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + aMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + } else { + *thunk = true; + } + break; + + case lir::Move: + aMask.typeMask = ~0; + aMask.registerMask = ~static_cast(0); + + if (TargetBytesPerWord == 4) { + if (aSize == 4 and bSize == 8) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + const uint32_t mask + = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); + aMask.registerMask = (static_cast(mask) << 32) | mask; + } else if (aSize == 1 or bSize == 1) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + const uint32_t mask + = (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx); + aMask.registerMask = (static_cast(mask) << 32) | mask; + } + } + break; + + default: + break; + } + } + + virtual void planDestination + (lir::BinaryOperation op, + unsigned aSize, const OperandMask& aMask, + unsigned bSize, OperandMask& bMask) + { + bMask.typeMask = ~0; + bMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + + switch (op) { + case lir::Absolute: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = (static_cast(1) << rax); + break; + + case lir::FloatAbsolute: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = aMask.registerMask; + break; + + case lir::Negate: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = aMask.registerMask; + break; + + case lir::FloatNegate: + case lir::FloatSquareRoot: + case lir::Float2Float: + case lir::Int2Float: + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + break; + + case lir::Float2Int: + bMask.typeMask = (1 << lir::RegisterOperand); + break; + + case lir::Move: + if (aMask.typeMask & ((1 << lir::MemoryOperand) | 1 << lir::AddressOperand)) { + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32) + | FloatRegisterMask; + } else if (aMask.typeMask & (1 << lir::RegisterOperand)) { + bMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + if (aMask.registerMask & FloatRegisterMask) { + bMask.registerMask = FloatRegisterMask; + } else { + bMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + } + } else { + bMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + } + + if (TargetBytesPerWord == 4) { + if (aSize == 4 and bSize == 8) { + bMask.registerMask = (static_cast(1) << (rdx + 32)) + | (static_cast(1) << rax); + } else if (aSize == 1 or bSize == 1) { + const uint32_t mask + = (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx); + bMask.registerMask = (static_cast(mask) << 32) | mask; + } + } + break; + + default: + break; + } + } + + virtual void planMove + (unsigned size, OperandMask& srcMask, + OperandMask& tmpMask, + const OperandMask& dstMask) + { + srcMask.typeMask = ~0; + srcMask.registerMask = ~static_cast(0); + + tmpMask.typeMask = 0; + tmpMask.registerMask = 0; + + if (dstMask.typeMask & (1 << lir::MemoryOperand)) { + // can't move directly from memory to memory + srcMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + tmpMask.typeMask = 1 << lir::RegisterOperand; + tmpMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + } else if (dstMask.typeMask & (1 << lir::RegisterOperand)) { + if (size > TargetBytesPerWord) { + // can't move directly from FPR to GPR or vice-versa for + // values larger than the GPR size + if (dstMask.registerMask & FloatRegisterMask) { + srcMask.registerMask = FloatRegisterMask + | (static_cast(FloatRegisterMask) << 32); + tmpMask.typeMask = 1 << lir::MemoryOperand; + } else if (dstMask.registerMask & GeneralRegisterMask) { + srcMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + tmpMask.typeMask = 1 << lir::MemoryOperand; + } + } + if (dstMask.registerMask & FloatRegisterMask) { + // can't move directly from constant to FPR + srcMask.typeMask &= ~(1 << lir::ConstantOperand); + if (size > TargetBytesPerWord) { + tmpMask.typeMask = 1 << lir::MemoryOperand; + } else { + tmpMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + tmpMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + } + } + } + } + + virtual void planSource + (lir::TernaryOperation op, + unsigned aSize, OperandMask& aMask, + unsigned bSize, OperandMask& bMask, + unsigned, bool* thunk) + { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::ConstantOperand); + aMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + + bMask.typeMask = (1 << lir::RegisterOperand); + bMask.registerMask = GeneralRegisterMask + | (static_cast(GeneralRegisterMask) << 32); + + *thunk = false; + + switch (op) { + case lir::FloatAdd: + case lir::FloatSubtract: + case lir::FloatMultiply: + case lir::FloatDivide: + if (useSSE(&c)) { + aMask.typeMask = (1 << lir::RegisterOperand) | (1 << lir::MemoryOperand); + bMask.typeMask = (1 << lir::RegisterOperand); + + const uint64_t mask + = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + aMask.registerMask = mask; + bMask.registerMask = mask; + } else { + *thunk = true; + } + break; + + case lir::FloatRemainder: + *thunk = true; + break; + + case lir::Multiply: + if (TargetBytesPerWord == 4 and aSize == 8) { + const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); + aMask.registerMask = (static_cast(mask) << 32) | mask; + bMask.registerMask = (static_cast(1) << (rdx + 32)) | mask; + } else { + aMask.registerMask = GeneralRegisterMask; + bMask.registerMask = GeneralRegisterMask; + } + break; + + case lir::Divide: + if (TargetBytesPerWord == 4 and aSize == 8) { + *thunk = true; + } else { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); + bMask.registerMask = 1 << rax; + } + break; + + case lir::Remainder: + if (TargetBytesPerWord == 4 and aSize == 8) { + *thunk = true; + } else { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); + bMask.registerMask = 1 << rax; + } + break; + + case lir::ShiftLeft: + case lir::ShiftRight: + case lir::UnsignedShiftRight: { + if (TargetBytesPerWord == 4 and bSize == 8) { + const uint32_t mask = GeneralRegisterMask & ~(1 << rcx); + aMask.registerMask = (static_cast(mask) << 32) | mask; + bMask.registerMask = (static_cast(mask) << 32) | mask; + } else { + aMask.registerMask = (static_cast(GeneralRegisterMask) << 32) + | (static_cast(1) << rcx); + const uint32_t mask = GeneralRegisterMask & ~(1 << rcx); + bMask.registerMask = (static_cast(mask) << 32) | mask; + } + } break; + + case lir::JumpIfFloatEqual: + case lir::JumpIfFloatNotEqual: + case lir::JumpIfFloatLess: + case lir::JumpIfFloatGreater: + case lir::JumpIfFloatLessOrEqual: + case lir::JumpIfFloatGreaterOrEqual: + case lir::JumpIfFloatLessOrUnordered: + case lir::JumpIfFloatGreaterOrUnordered: + case lir::JumpIfFloatLessOrEqualOrUnordered: + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + if (useSSE(&c)) { + aMask.typeMask = (1 << lir::RegisterOperand); + aMask.registerMask = (static_cast(FloatRegisterMask) << 32) + | FloatRegisterMask; + bMask.typeMask = aMask.typeMask; + bMask.registerMask = aMask.registerMask; + } else { + *thunk = true; + } + break; + + default: + break; + } + } + + virtual void planDestination + (lir::TernaryOperation op, + unsigned, const OperandMask&, + unsigned, const OperandMask& bMask, + unsigned, OperandMask& cMask) + { + if (isBranch(op)) { + cMask.typeMask = (1 << lir::ConstantOperand); + cMask.registerMask = 0; + } else { + cMask.typeMask = (1 << lir::RegisterOperand); + cMask.registerMask = bMask.registerMask; + } + } + + virtual Assembler* makeAssembler(Allocator* allocator, Zone* zone); + + virtual void acquire() { + ++ referenceCount; + } + + virtual void release() { + if (-- referenceCount == 0) { + c.s->free(this); + } + } + + ArchitectureContext c; + unsigned referenceCount; + const RegisterFile myRegisterFile; +}; + +class MyAssembler: public Assembler { + public: + MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): + c(s, a, zone, &(arch->c)), arch_(arch) + { } + + virtual void setClient(Client* client) { + assert(&c, c.client == 0); + c.client = client; + } + + virtual Architecture* arch() { + return arch_; + } + + virtual void checkStackOverflow(uintptr_t handler, + unsigned stackLimitOffsetFromThread) + { + lir::Register stack(rsp); + lir::Memory stackLimit(rbx, stackLimitOffsetFromThread); + lir::Constant handlerConstant(resolvedPromise(&c, handler)); + branchRM(&c, lir::JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, + &handlerConstant); + } + + virtual void saveFrame(unsigned stackOffset, unsigned) { + lir::Register stack(rsp); + lir::Memory stackDst(rbx, stackOffset); + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack), + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &stackDst)); + } + + virtual void pushFrame(unsigned argumentCount, ...) { + // TODO: Argument should be replaced by OperandInfo... + struct Argument { + unsigned size; + lir::OperandType type; + lir::Operand* operand; + }; + RUNTIME_ARRAY(Argument, arguments, argumentCount); + va_list a; va_start(a, argumentCount); + unsigned footprint = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned); + RUNTIME_ARRAY_BODY(arguments)[i].type + = static_cast(va_arg(a, int)); + RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, lir::Operand*); + footprint += ceilingDivide + (RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); + } + va_end(a); + + allocateFrame(arch_->alignFrameSize(footprint)); + + unsigned offset = 0; + for (unsigned i = 0; i < argumentCount; ++i) { + if (i < arch_->argumentRegisterCount()) { + lir::Register dst(arch_->argumentRegister(i)); + apply(lir::Move, + OperandInfo( + RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo( + pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), + lir::RegisterOperand, + &dst)); + } else { + lir::Memory dst(rsp, offset * TargetBytesPerWord); + apply(lir::Move, + OperandInfo( + RUNTIME_ARRAY_BODY(arguments)[i].size, + RUNTIME_ARRAY_BODY(arguments)[i].type, + RUNTIME_ARRAY_BODY(arguments)[i].operand), + OperandInfo( + pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), + lir::MemoryOperand, + &dst)); + offset += ceilingDivide + (RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); + } + } + } + + virtual void allocateFrame(unsigned footprint) { + lir::Register stack(rsp); + + if (UseFramePointer) { + lir::Register base(rbp); + pushR(&c, TargetBytesPerWord, &base); + + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &base)); + } + + lir::Constant footprintConstant(resolvedPromise(&c, footprint * TargetBytesPerWord)); + apply(lir::Subtract, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &footprintConstant), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack)); + } + + virtual void adjustFrame(unsigned difference) { + lir::Register stack(rsp); + lir::Constant differenceConstant(resolvedPromise(&c, difference * TargetBytesPerWord)); + apply(lir::Subtract, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &differenceConstant), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack)); + } + + virtual void popFrame(unsigned frameFootprint) { + if (UseFramePointer) { + lir::Register base(rbp); + lir::Register stack(rsp); + apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &base), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack)); + + popR(&c, TargetBytesPerWord, &base); + } else { + lir::Register stack(rsp); + lir::Constant footprint(resolvedPromise(&c, frameFootprint * TargetBytesPerWord)); + apply(lir::Add, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &footprint), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &stack)); + } + } + + virtual void popFrameForTailCall(unsigned frameFootprint, + int offset, + int returnAddressSurrogate, + int framePointerSurrogate) + { + if (TailCalls) { + if (offset) { + lir::Register tmp(c.client->acquireTemporary()); + + unsigned baseSize = UseFramePointer ? 1 : 0; + + lir::Memory returnAddressSrc + (rsp, (frameFootprint + baseSize) * TargetBytesPerWord); + moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, + &tmp); + + lir::Memory returnAddressDst + (rsp, (frameFootprint - offset + baseSize) * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, + &returnAddressDst); + + c.client->releaseTemporary(tmp.low); + + if (UseFramePointer) { + lir::Memory baseSrc(rsp, frameFootprint * TargetBytesPerWord); + lir::Register base(rbp); + moveMR(&c, TargetBytesPerWord, &baseSrc, TargetBytesPerWord, &base); + } + + lir::Register stack(rsp); + lir::Constant footprint + (resolvedPromise + (&c, (frameFootprint - offset + baseSize) * TargetBytesPerWord)); + + addCR(&c, TargetBytesPerWord, &footprint, TargetBytesPerWord, &stack); + + if (returnAddressSurrogate != lir::NoRegister) { + assert(&c, offset > 0); + + lir::Register ras(returnAddressSurrogate); + lir::Memory dst(rsp, offset * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); + } + + if (framePointerSurrogate != lir::NoRegister) { + assert(&c, offset > 0); + + lir::Register fps(framePointerSurrogate); + lir::Memory dst(rsp, (offset - 1) * TargetBytesPerWord); + moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst); + } + } else { + popFrame(frameFootprint); + } + } else { + abort(&c); + } + } + + virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, + unsigned argumentFootprint) + { + popFrame(frameFootprint); + + assert(&c, argumentFootprint >= StackAlignmentInWords); + assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); + + if (TailCalls and argumentFootprint > StackAlignmentInWords) { + lir::Register returnAddress(rcx); + popR(&c, TargetBytesPerWord, &returnAddress); + + lir::Register stack(rsp); + lir::Constant adjustment + (resolvedPromise(&c, (argumentFootprint - StackAlignmentInWords) + * TargetBytesPerWord)); + addCR(&c, TargetBytesPerWord, &adjustment, TargetBytesPerWord, &stack); + + jumpR(&c, TargetBytesPerWord, &returnAddress); + } else { + return_(&c); + } + } + + virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, + unsigned stackOffsetFromThread) + { + popFrame(frameFootprint); + + lir::Register returnAddress(rcx); + popR(&c, TargetBytesPerWord, &returnAddress); + + lir::Register stack(rsp); + lir::Memory stackSrc(rbx, stackOffsetFromThread); + moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack); + + jumpR(&c, TargetBytesPerWord, &returnAddress); + } + + virtual void apply(lir::Operation op) { + arch_->c.operations[op](&c); + } + + virtual void apply(lir::UnaryOperation op, OperandInfo a) + { + arch_->c.unaryOperations[Multimethod::index(op, a.type)] + (&c, a.size, a.operand); + } + + virtual void apply(lir::BinaryOperation op, OperandInfo a, OperandInfo b) + { + arch_->c.binaryOperations[index(&(arch_->c), op, a.type, b.type)] + (&c, a.size, a.operand, b.size, b.operand); + } + + virtual void apply(lir::TernaryOperation op, OperandInfo a, OperandInfo b, OperandInfo c) + { + if (isBranch(op)) { + assert(&this->c, a.size == b.size); + assert(&this->c, c.size == TargetBytesPerWord); + assert(&this->c, c.type == lir::ConstantOperand); + + arch_->c.branchOperations[branchIndex(&(arch_->c), a.type, b.type)] + (&this->c, op, a.size, a.operand, b.operand, c.operand); + } else { + assert(&this->c, b.size == c.size); + assert(&this->c, b.type == c.type); + + arch_->c.binaryOperations[index(&(arch_->c), op, a.type, b.type)] + (&this->c, a.size, a.operand, b.size, b.operand); + } + } + + virtual void setDestination(uint8_t* dst) { + c.result = dst; + } + + virtual void write() { + uint8_t* dst = c.result; + for (MyBlock* b = c.firstBlock; b; b = b->next) { + unsigned index = 0; + unsigned padding = 0; + for (AlignmentPadding* p = b->firstPadding; p; p = p->next) { + unsigned size = p->offset - b->offset - index; + + memcpy(dst + b->start + index + padding, + c.code.data + b->offset + index, + size); + + index += size; + + while ((b->start + index + padding + p->instructionOffset) + % p->alignment) + { + *(dst + b->start + index + padding) = 0x90; + ++ padding; + } + } + + memcpy(dst + b->start + index + padding, + c.code.data + b->offset + index, + b->size - index); + } + + for (Task* t = c.tasks; t; t = t->next) { + t->run(&c); + } + } + + virtual Promise* offset(bool) { + return x86::offsetPromise(&c); + } + + virtual Block* endBlock(bool startNew) { + MyBlock* b = c.lastBlock; + b->size = c.code.length() - b->offset; + if (startNew) { + c.lastBlock = new(c.zone) MyBlock(c.code.length()); + } else { + c.lastBlock = 0; + } + return b; + } + + virtual void endEvent() { + // ignore + } + + virtual unsigned length() { + return c.code.length(); + } + + virtual unsigned footerSize() { + return 0; + } + + virtual void dispose() { + c.code.dispose(); + } + + Context c; + MyArchitecture* arch_; +}; + +Assembler* MyArchitecture::makeAssembler(Allocator* allocator, Zone* zone) { + return + new(zone) MyAssembler(c.s, allocator, zone, this); +} + +} // namespace x86 + +Architecture* makeArchitectureX86(System* system, bool useNativeFeatures) +{ + return new (allocate(system, sizeof(x86::MyArchitecture))) + x86::MyArchitecture(system, useNativeFeatures); +} + +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/block.cpp b/src/codegen/target/x86/block.cpp new file mode 100644 index 0000000000..5c2b125283 --- /dev/null +++ b/src/codegen/target/x86/block.cpp @@ -0,0 +1,36 @@ +/* Copyright (c) 2008-2012, 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 "block.h" + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +unsigned +padding(AlignmentPadding* p, unsigned index, unsigned offset, AlignmentPadding* limit); + +MyBlock::MyBlock(unsigned offset): + next(0), firstPadding(0), lastPadding(0), offset(offset), start(~0), + size(0) +{ } + +unsigned MyBlock::resolve(unsigned start, Assembler::Block* next) { + this->start = start; + this->next = static_cast(next); + + return start + size + padding(firstPadding, start, offset, lastPadding); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/block.h b/src/codegen/target/x86/block.h new file mode 100644 index 0000000000..211cf9d9f1 --- /dev/null +++ b/src/codegen/target/x86/block.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class AlignmentPadding; + +class MyBlock: public Assembler::Block { + public: + MyBlock(unsigned offset); + + virtual unsigned resolve(unsigned start, Assembler::Block* next); + + MyBlock* next; + AlignmentPadding* firstPadding; + AlignmentPadding* lastPadding; + unsigned offset; + unsigned start; + unsigned size; +}; + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_BLOCK_H diff --git a/src/codegen/target/x86/context.cpp b/src/codegen/target/x86/context.cpp new file mode 100644 index 0000000000..535f8c50ef --- /dev/null +++ b/src/codegen/target/x86/context.cpp @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2012, 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 "avian/allocator.h" +#include "avian/zone.h" + +#include "context.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +ArchitectureContext::ArchitectureContext(vm::System* s, bool useNativeFeatures): + s(s), useNativeFeatures(useNativeFeatures) +{ } + +Context::Context(vm::System* s, vm::Allocator* a, vm::Zone* zone, ArchitectureContext* ac): + s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), + firstBlock(new(zone) MyBlock(0)), + lastBlock(firstBlock), ac(ac) +{ } + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/context.h b/src/codegen/target/x86/context.h new file mode 100644 index 0000000000..543db7225e --- /dev/null +++ b/src/codegen/target/x86/context.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H + +#define CAST1(x) reinterpret_cast(x) +#define CAST2(x) reinterpret_cast(x) +#define CAST_BRANCH(x) reinterpret_cast(x) + +#include + +#include "avian/alloc-vector.h" + +#include +#include + +#include + +namespace vm { +class System; +class Allocator; +class Zone; +} // namespace vm + +namespace avian { + +namespace util { +class Aborter; +} // namespace util + +namespace codegen { +namespace x86 { + +class Context; +class MyBlock; +class Task; + +typedef void (*OperationType)(Context*); + +typedef void (*UnaryOperationType)(Context*, unsigned, lir::Operand*); + +typedef void (*BinaryOperationType) +(Context*, unsigned, lir::Operand*, unsigned, lir::Operand*); + +typedef void (*BranchOperationType) +(Context*, lir::TernaryOperation, unsigned, lir::Operand*, + lir::Operand*, lir::Operand*); + +class ArchitectureContext { + public: + ArchitectureContext(vm::System* s, bool useNativeFeatures); + + vm::System* s; + bool useNativeFeatures; + OperationType operations[lir::OperationCount]; + UnaryOperationType unaryOperations[lir::UnaryOperationCount + * lir::OperandTypeCount]; + BinaryOperationType binaryOperations + [(lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::OperandTypeCount + * lir::OperandTypeCount]; + BranchOperationType branchOperations + [lir::BranchOperationCount + * lir::OperandTypeCount + * lir::OperandTypeCount]; +}; + +class Context { + public: + Context(vm::System* s, vm::Allocator* a, vm::Zone* zone, ArchitectureContext* ac); + + vm::System* s; + vm::Zone* zone; + Assembler::Client* client; + vm::Vector code; + Task* tasks; + uint8_t* result; + MyBlock* firstBlock; + MyBlock* lastBlock; + ArchitectureContext* ac; +}; + +inline avian::util::Aborter* getAborter(Context* c) { + return c->s; +} + +inline avian::util::Aborter* getAborter(ArchitectureContext* c) { + return c->s; +} + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_CONTEXT_H diff --git a/src/codegen/target/x86/detect.cpp b/src/codegen/target/x86/detect.cpp new file mode 100644 index 0000000000..4f2c62d1e8 --- /dev/null +++ b/src/codegen/target/x86/detect.cpp @@ -0,0 +1,41 @@ + +/* Copyright (c) 2008-2012, 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 "avian/target.h" + +#include "context.h" + +namespace avian { +namespace codegen { +namespace x86 { + +extern "C" bool +detectFeature(unsigned ecx, unsigned edx); + +bool useSSE(ArchitectureContext* c) { + if (vm::TargetBytesPerWord == 8) { + // amd64 implies SSE2 support + return true; + } else if (c->useNativeFeatures) { + static int supported = -1; + if (supported == -1) { + supported = detectFeature(0, 0x2000000) // SSE 1 + and detectFeature(0, 0x4000000); // SSE 2 + } + return supported; + } else { + return false; + } +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/detect.h b/src/codegen/target/x86/detect.h new file mode 100644 index 0000000000..b7c3b0aa8f --- /dev/null +++ b/src/codegen/target/x86/detect.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class ArchitectureContext; + +bool useSSE(ArchitectureContext* c); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_DETECT_H diff --git a/src/codegen/target/x86/encode.cpp b/src/codegen/target/x86/encode.cpp new file mode 100644 index 0000000000..31852f4269 --- /dev/null +++ b/src/codegen/target/x86/encode.cpp @@ -0,0 +1,378 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" +#include "avian/alloc-vector.h" + +#include +#include + +#include +#include + +#include "context.h" +#include "encode.h" +#include "registers.h" +#include "fixup.h" + +using namespace avian::util; + +namespace { + +int64_t +signExtend(unsigned size, int64_t v) +{ + if (size == 4) { + return static_cast(v); + } else if (size == 2) { + return static_cast(v); + } else if (size == 1) { + return static_cast(v); + } else { + return v; + } +} + +} // namespace + +namespace avian { +namespace codegen { +namespace x86 { + + +#define REX_W 0x48 +#define REX_R 0x44 +#define REX_X 0x42 +#define REX_B 0x41 +#define REX_NONE 0x40 + +void maybeRex(Context* c, unsigned size, int a, int index, int base, bool always) { + if (vm::TargetBytesPerWord == 8) { + uint8_t byte; + if (size == 8) { + byte = REX_W; + } else { + byte = REX_NONE; + } + if (a != lir::NoRegister and (a & 8)) byte |= REX_R; + if (index != lir::NoRegister and (index & 8)) byte |= REX_X; + if (base != lir::NoRegister and (base & 8)) byte |= REX_B; + if (always or byte != REX_NONE) c->code.append(byte); + } +} + +void maybeRex(Context* c, unsigned size, lir::Register* a, lir::Register* b) { + maybeRex(c, size, a->low, lir::NoRegister, b->low, false); +} + +void alwaysRex(Context* c, unsigned size, lir::Register* a, lir::Register* b) { + maybeRex(c, size, a->low, lir::NoRegister, b->low, true); +} + +void maybeRex(Context* c, unsigned size, lir::Register* a) { + maybeRex(c, size, lir::NoRegister, lir::NoRegister, a->low, false); +} + +void maybeRex(Context* c, unsigned size, lir::Register* a, lir::Memory* b) { + maybeRex(c, size, a->low, b->index, b->base, size == 1 and (a->low & 4)); +} + +void maybeRex(Context* c, unsigned size, lir::Memory* a) { + maybeRex(c, size, lir::NoRegister, a->index, a->base, false); +} + +void modrm(Context* c, uint8_t mod, int a, int b) { + c->code.append(mod | (regCode(b) << 3) | regCode(a)); +} + +void modrm(Context* c, uint8_t mod, lir::Register* a, lir::Register* b) { + modrm(c, mod, a->low, b->low); +} + +void sib(Context* c, unsigned scale, int index, int base) { + c->code.append((util::log(scale) << 6) | (regCode(index) << 3) | regCode(base)); +} + +void modrmSib(Context* c, int width, int a, int scale, int index, int base) { + if (index == lir::NoRegister) { + modrm(c, width, base, a); + if (regCode(base) == rsp) { + sib(c, 0x00, rsp, rsp); + } + } else { + modrm(c, width, rsp, a); + sib(c, scale, index, base); + } +} + +void modrmSibImm(Context* c, int a, int scale, int index, int base, int offset) { + if (offset == 0 and regCode(base) != rbp) { + modrmSib(c, 0x00, a, scale, index, base); + } else if (vm::fitsInInt8(offset)) { + modrmSib(c, 0x40, a, scale, index, base); + c->code.append(offset); + } else { + modrmSib(c, 0x80, a, scale, index, base); + c->code.append4(offset); + } +} + +void modrmSibImm(Context* c, lir::Register* a, lir::Memory* b) { + modrmSibImm(c, a->low, b->scale, b->index, b->base, b->offset); +} + +void opcode(Context* c, uint8_t op) { + c->code.append(op); +} + +void opcode(Context* c, uint8_t op1, uint8_t op2) { + c->code.append(op1); + c->code.append(op2); +} + +void unconditional(Context* c, unsigned jump, lir::Constant* a) { + appendOffsetTask(c, a->value, offsetPromise(c), 5); + + opcode(c, jump); + c->code.append4(0); +} + +void conditional(Context* c, unsigned condition, lir::Constant* a) { + appendOffsetTask(c, a->value, offsetPromise(c), 6); + + opcode(c, 0x0f, condition); + c->code.append4(0); +} + +void sseMoveRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize >= 4); + assert(c, aSize == bSize); + + if (isFloatReg(a) and isFloatReg(b)) { + if (aSize == 4) { + opcode(c, 0xf3); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x10); + modrm(c, 0xc0, a, b); + } else { + opcode(c, 0xf2); + maybeRex(c, 4, b, a); + opcode(c, 0x0f, 0x10); + modrm(c, 0xc0, a, b); + } + } else if (isFloatReg(a)) { + opcode(c, 0x66); + maybeRex(c, aSize, a, b); + opcode(c, 0x0f, 0x7e); + modrm(c, 0xc0, b, a); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0x6e); + modrm(c, 0xc0, a, b); + } +} + +void sseMoveCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize <= vm::TargetBytesPerWord); + lir::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR2(c, aSize, a, aSize, &tmp, 0); + sseMoveRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); +} + +void sseMoveMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize >= 4); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + opcode(c, 0xf3); + opcode(c, 0x0f, 0x7e); + modrmSibImm(c, b, a); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0x6e); + modrmSibImm(c, b, a); + } +} + +void sseMoveRM(Context* c, unsigned aSize, lir::Register* a, + UNUSED unsigned bSize, lir::Memory* b) +{ + assert(c, aSize >= 4); + assert(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + opcode(c, 0x66); + opcode(c, 0x0f, 0xd6); + modrmSibImm(c, a, b); + } else { + opcode(c, 0x66); + maybeRex(c, aSize, a, b); + opcode(c, 0x0f, 0x7e); + modrmSibImm(c, a, b); + } +} + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target) { + switch (op) { + case lir::JumpIfEqual: + conditional(c, 0x84, target); + break; + + case lir::JumpIfNotEqual: + conditional(c, 0x85, target); + break; + + case lir::JumpIfLess: + conditional(c, 0x8c, target); + break; + + case lir::JumpIfGreater: + conditional(c, 0x8f, target); + break; + + case lir::JumpIfLessOrEqual: + conditional(c, 0x8e, target); + break; + + case lir::JumpIfGreaterOrEqual: + conditional(c, 0x8d, target); + break; + + default: + abort(c); + } +} + +void branchFloat(Context* c, lir::TernaryOperation op, lir::Constant* target) { + switch (op) { + case lir::JumpIfFloatEqual: + // jp past the je so we don't jump to the target if unordered: + c->code.append(0x7a); + c->code.append(6); + conditional(c, 0x84, target); + break; + + case lir::JumpIfFloatNotEqual: + conditional(c, 0x85, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatLess: + conditional(c, 0x82, target); + break; + + case lir::JumpIfFloatGreater: + conditional(c, 0x87, target); + break; + + case lir::JumpIfFloatLessOrEqual: + conditional(c, 0x86, target); + break; + + case lir::JumpIfFloatGreaterOrEqual: + conditional(c, 0x83, target); + break; + + case lir::JumpIfFloatLessOrUnordered: + conditional(c, 0x82, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatGreaterOrUnordered: + conditional(c, 0x87, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatLessOrEqualOrUnordered: + conditional(c, 0x86, target); + conditional(c, 0x8a, target); + break; + + case lir::JumpIfFloatGreaterOrEqualOrUnordered: + conditional(c, 0x83, target); + conditional(c, 0x8a, target); + break; + + default: + abort(c); + } +} + +void floatRegOp(Context* c, unsigned aSize, lir::Register* a, unsigned bSize, + lir::Register* b, uint8_t op, uint8_t mod) +{ + if (aSize == 4) { + opcode(c, 0xf3); + } else { + opcode(c, 0xf2); + } + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, op); + modrm(c, mod, a, b); +} + +void floatMemOp(Context* c, unsigned aSize, lir::Memory* a, unsigned bSize, + lir::Register* b, uint8_t op) +{ + if (aSize == 4) { + opcode(c, 0xf3); + } else { + opcode(c, 0xf2); + } + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, op); + modrmSibImm(c, b, a); +} + +void moveCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void moveCR2(Context* c, UNUSED unsigned aSize, lir::Constant* a, + UNUSED unsigned bSize, lir::Register* b, unsigned promiseOffset) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + int64_t v = signExtend(aSize, a->value->value()); + + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + moveCR(c, 4, &al, 4, b); + moveCR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, vm::TargetBytesPerWord, b); + opcode(c, 0xb8 + regCode(b)); + if (a->value->resolved()) { + c->code.appendTargetAddress(signExtend(aSize, a->value->value())); + } else { + expect(c, aSize == vm::TargetBytesPerWord); + + appendImmediateTask + (c, a->value, offsetPromise(c), vm::TargetBytesPerWord, promiseOffset); + c->code.appendTargetAddress(static_cast(0)); + } + } +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/encode.h b/src/codegen/target/x86/encode.h new file mode 100644 index 0000000000..fea6913e45 --- /dev/null +++ b/src/codegen/target/x86/encode.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H + +#include + +#include "avian/common.h" + +#include + +#include "registers.h" + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; + +void maybeRex(Context* c, unsigned size, int a, int index, int base, bool always); + +void maybeRex(Context* c, unsigned size, lir::Register* a, lir::Register* b); + +void alwaysRex(Context* c, unsigned size, lir::Register* a, lir::Register* b); + +void maybeRex(Context* c, unsigned size, lir::Register* a); + +void maybeRex(Context* c, unsigned size, lir::Register* a, lir::Memory* b); + +void maybeRex(Context* c, unsigned size, lir::Memory* a); + +inline int regCode(int a) { + return a & 7; +} + +inline int regCode(lir::Register* a) { + return regCode(a->low); +} + +inline bool isFloatReg(lir::Register* a) { + return a->low >= xmm0; +} + +void modrm(Context* c, uint8_t mod, int a, int b); + +void modrm(Context* c, uint8_t mod, lir::Register* a, lir::Register* b); + +void sib(Context* c, unsigned scale, int index, int base); + +void modrmSib(Context* c, int width, int a, int scale, int index, int base); + +void modrmSibImm(Context* c, int a, int scale, int index, int base, int offset); + +void modrmSibImm(Context* c, lir::Register* a, lir::Memory* b); + +void opcode(Context* c, uint8_t op); + +void opcode(Context* c, uint8_t op1, uint8_t op2); + +void unconditional(Context* c, unsigned jump, lir::Constant* a); + +void conditional(Context* c, unsigned condition, lir::Constant* a); + +void sseMoveRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void sseMoveCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void sseMoveMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void sseMoveRM(Context* c, unsigned aSize, lir::Register* a, + UNUSED unsigned bSize, lir::Memory* b); + +void branch(Context* c, lir::TernaryOperation op, lir::Constant* target); + +void branchFloat(Context* c, lir::TernaryOperation op, lir::Constant* target); + +void floatRegOp(Context* c, unsigned aSize, lir::Register* a, unsigned bSize, + lir::Register* b, uint8_t op, uint8_t mod = 0xc0); + +void floatMemOp(Context* c, unsigned aSize, lir::Memory* a, unsigned bSize, + lir::Register* b, uint8_t op); + +void moveCR2(Context* c, UNUSED unsigned aSize, lir::Constant* a, + UNUSED unsigned bSize, lir::Register* b, unsigned promiseOffset); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_ENCODE_H diff --git a/src/codegen/target/x86/fixup.cpp b/src/codegen/target/x86/fixup.cpp new file mode 100644 index 0000000000..0a81635510 --- /dev/null +++ b/src/codegen/target/x86/fixup.cpp @@ -0,0 +1,173 @@ +/* Copyright (c) 2008-2012, 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 + +#include "avian/allocator.h" +#include "avian/alloc-vector.h" +#include "avian/common.h" +#include "avian/zone.h" + +#include +#include + +#include "context.h" +#include "fixup.h" +#include "padding.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +using namespace util; + +ResolvedPromise* resolvedPromise(Context* c, int64_t value) { + return new(c->zone) ResolvedPromise(value); +} + +OffsetPromise::OffsetPromise(Context* c, MyBlock* block, unsigned offset, AlignmentPadding* limit): + c(c), block(block), offset(offset), limit(limit), value_(-1) +{ } + +bool OffsetPromise::resolved() { + return block->start != static_cast(~0); +} + +int64_t OffsetPromise::value() { + assert(c, resolved()); + + if (value_ == -1) { + value_ = block->start + (offset - block->offset) + + padding(block->firstPadding, block->start, block->offset, limit); + } + + return value_; +} +Promise* offsetPromise(Context* c) { + return new(c->zone) OffsetPromise(c, c->lastBlock, c->code.length(), c->lastBlock->lastPadding); +} + +void* +resolveOffset(vm::System* s, uint8_t* instruction, unsigned instructionSize, + int64_t value) +{ + intptr_t v = reinterpret_cast(value) + - instruction - instructionSize; + + expect(s, vm::fitsInInt32(v)); + + int32_t v4 = v; + memcpy(instruction + instructionSize - 4, &v4, 4); + return instruction + instructionSize; +} + +OffsetListener::OffsetListener(vm::System* s, uint8_t* instruction, + unsigned instructionSize): + s(s), + instruction(instruction), + instructionSize(instructionSize) +{ } + +bool OffsetListener::resolve(int64_t value, void** location) { + void* p = resolveOffset(s, instruction, instructionSize, value); + if (location) *location = p; + return false; +} + +OffsetTask::OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, + unsigned instructionSize): + Task(next), + promise(promise), + instructionOffset(instructionOffset), + instructionSize(instructionSize) +{ } + +void OffsetTask::run(Context* c) { + if (promise->resolved()) { + resolveOffset + (c->s, c->result + instructionOffset->value(), instructionSize, + promise->value()); + } else { + new (promise->listen(sizeof(OffsetListener))) + OffsetListener(c->s, c->result + instructionOffset->value(), + instructionSize); + } +} + +void +appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, + unsigned instructionSize) +{ + OffsetTask* task = + new(c->zone) OffsetTask(c->tasks, promise, instructionOffset, instructionSize); + + c->tasks = task; +} + +ImmediateListener::ImmediateListener(vm::System* s, void* dst, unsigned size, unsigned offset): + s(s), dst(dst), size(size), offset(offset) +{ } + +void copy(vm::System* s, void* dst, int64_t src, unsigned size) { + switch (size) { + case 4: { + int32_t v = src; + memcpy(dst, &v, 4); + } break; + + case 8: { + int64_t v = src; + memcpy(dst, &v, 8); + } break; + + default: abort(s); + } +} + +bool ImmediateListener::resolve(int64_t value, void** location) { + copy(s, dst, value, size); + if (location) *location = static_cast(dst) + offset; + return offset == 0; +} + +ImmediateTask::ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, + unsigned promiseOffset): + Task(next), + promise(promise), + offset(offset), + size(size), + promiseOffset(promiseOffset) +{ } + +void ImmediateTask::run(Context* c) { + if (promise->resolved()) { + copy(c->s, c->result + offset->value(), promise->value(), size); + } else { + new (promise->listen(sizeof(ImmediateListener))) ImmediateListener + (c->s, c->result + offset->value(), size, promiseOffset); + } +} + +void +appendImmediateTask(Context* c, Promise* promise, Promise* offset, + unsigned size, unsigned promiseOffset) +{ + c->tasks = new(c->zone) ImmediateTask + (c->tasks, promise, offset, size, promiseOffset); +} + +ShiftMaskPromise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) { + return new(c->zone) ShiftMaskPromise(base, shift, mask); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/fixup.h b/src/codegen/target/x86/fixup.h new file mode 100644 index 0000000000..fa170bb542 --- /dev/null +++ b/src/codegen/target/x86/fixup.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H + +#include + +#include + +namespace vm { +class System; +} + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; +class MyBlock; +class AlignmentPadding; + +ResolvedPromise* resolvedPromise(Context* c, int64_t value); + +class Task { + public: + Task(Task* next): next(next) { } + + virtual void run(Context* c) = 0; + + Task* next; +}; + +class OffsetPromise: public Promise { + public: + OffsetPromise(Context* c, MyBlock* block, unsigned offset, AlignmentPadding* limit); + + virtual bool resolved(); + + virtual int64_t value(); + + Context* c; + MyBlock* block; + unsigned offset; + AlignmentPadding* limit; + int value_; +}; + +Promise* offsetPromise(Context* c); + +void* resolveOffset(vm::System* s, uint8_t* instruction, unsigned instructionSize, int64_t value); + +class OffsetListener: public Promise::Listener { + public: + OffsetListener(vm::System* s, uint8_t* instruction, unsigned instructionSize); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + uint8_t* instruction; + unsigned instructionSize; +}; + +class OffsetTask: public Task { + public: + OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, unsigned instructionSize); + + virtual void run(Context* c); + + Promise* promise; + Promise* instructionOffset; + unsigned instructionSize; +}; + +void appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, unsigned instructionSize); + +class ImmediateListener: public Promise::Listener { + public: + ImmediateListener(vm::System* s, void* dst, unsigned size, unsigned offset); + + virtual bool resolve(int64_t value, void** location); + + vm::System* s; + void* dst; + unsigned size; + unsigned offset; +}; + +class ImmediateTask: public Task { + public: + ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, + unsigned promiseOffset); + + virtual void run(Context* c); + + Promise* promise; + Promise* offset; + unsigned size; + unsigned promiseOffset; +}; + +void +appendImmediateTask(Context* c, Promise* promise, Promise* offset, + unsigned size, unsigned promiseOffset = 0); + +ShiftMaskPromise* shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_FIXUP_H diff --git a/src/codegen/target/x86/multimethod.cpp b/src/codegen/target/x86/multimethod.cpp new file mode 100644 index 0000000000..5086d27db7 --- /dev/null +++ b/src/codegen/target/x86/multimethod.cpp @@ -0,0 +1,175 @@ +/* Copyright (c) 2008-2012, 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 "avian/common.h" + +#include + +#include + +#include "context.h" +#include "operations.h" + +#include "multimethod.h" +#include "../multimethod.h" + +namespace avian { +namespace codegen { +namespace x86 { + +using namespace util; + +unsigned index(ArchitectureContext*, lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2) +{ + return operation + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) * operand1) + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::OperandTypeCount * operand2); +} + +unsigned index(ArchitectureContext* c UNUSED, lir::TernaryOperation operation, + lir::OperandType operand1, lir::OperandType operand2) +{ + assert(c, not isBranch(operation)); + + return lir::BinaryOperationCount + operation + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) * operand1) + + ((lir::BinaryOperationCount + lir::NonBranchTernaryOperationCount) + * lir::OperandTypeCount * operand2); +} + +unsigned branchIndex(ArchitectureContext* c UNUSED, lir::OperandType operand1, + lir::OperandType operand2) +{ + return operand1 + (lir::OperandTypeCount * operand2); +} + + +void populateTables(ArchitectureContext* c) { + const lir::OperandType C = lir::ConstantOperand; + const lir::OperandType A = lir::AddressOperand; + const lir::OperandType R = lir::RegisterOperand; + const lir::OperandType M = lir::MemoryOperand; + + OperationType* zo = c->operations; + UnaryOperationType* uo = c->unaryOperations; + BinaryOperationType* bo = c->binaryOperations; + BranchOperationType* bro = c->branchOperations; + + zo[lir::Return] = return_; + zo[lir::LoadBarrier] = ignore; + zo[lir::StoreStoreBarrier] = ignore; + zo[lir::StoreLoadBarrier] = storeLoadBarrier; + zo[lir::Trap] = trap; + + uo[Multimethod::index(lir::Call, C)] = CAST1(callC); + uo[Multimethod::index(lir::Call, R)] = CAST1(callR); + uo[Multimethod::index(lir::Call, M)] = CAST1(callM); + + uo[Multimethod::index(lir::AlignedCall, C)] = CAST1(alignedCallC); + + uo[Multimethod::index(lir::LongCall, C)] = CAST1(longCallC); + + uo[Multimethod::index(lir::AlignedLongCall, C)] = CAST1(alignedLongCallC); + + uo[Multimethod::index(lir::Jump, R)] = CAST1(jumpR); + uo[Multimethod::index(lir::Jump, C)] = CAST1(jumpC); + uo[Multimethod::index(lir::Jump, M)] = CAST1(jumpM); + + uo[Multimethod::index(lir::AlignedJump, C)] = CAST1(alignedJumpC); + + uo[Multimethod::index(lir::LongJump, C)] = CAST1(longJumpC); + + uo[Multimethod::index(lir::AlignedLongJump, C)] = CAST1(alignedLongJumpC); + + bo[index(c, lir::Negate, R, R)] = CAST2(negateRR); + + bo[index(c, lir::FloatNegate, R, R)] = CAST2(floatNegateRR); + + bo[index(c, lir::Move, R, R)] = CAST2(moveRR); + bo[index(c, lir::Move, C, R)] = CAST2(moveCR); + bo[index(c, lir::Move, M, R)] = CAST2(moveMR); + bo[index(c, lir::Move, R, M)] = CAST2(moveRM); + bo[index(c, lir::Move, C, M)] = CAST2(moveCM); + bo[index(c, lir::Move, A, R)] = CAST2(moveAR); + + bo[index(c, lir::FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); + bo[index(c, lir::FloatSquareRoot, M, R)] = CAST2(floatSqrtMR); + + bo[index(c, lir::MoveZ, R, R)] = CAST2(moveZRR); + bo[index(c, lir::MoveZ, M, R)] = CAST2(moveZMR); + bo[index(c, lir::MoveZ, C, R)] = CAST2(moveZCR); + + bo[index(c, lir::Add, R, R)] = CAST2(addRR); + bo[index(c, lir::Add, C, R)] = CAST2(addCR); + + bo[index(c, lir::Subtract, C, R)] = CAST2(subtractCR); + bo[index(c, lir::Subtract, R, R)] = CAST2(subtractRR); + + bo[index(c, lir::FloatAdd, R, R)] = CAST2(floatAddRR); + bo[index(c, lir::FloatAdd, M, R)] = CAST2(floatAddMR); + + bo[index(c, lir::FloatSubtract, R, R)] = CAST2(floatSubtractRR); + bo[index(c, lir::FloatSubtract, M, R)] = CAST2(floatSubtractMR); + + bo[index(c, lir::And, R, R)] = CAST2(andRR); + bo[index(c, lir::And, C, R)] = CAST2(andCR); + + bo[index(c, lir::Or, R, R)] = CAST2(orRR); + bo[index(c, lir::Or, C, R)] = CAST2(orCR); + + bo[index(c, lir::Xor, R, R)] = CAST2(xorRR); + bo[index(c, lir::Xor, C, R)] = CAST2(xorCR); + + bo[index(c, lir::Multiply, R, R)] = CAST2(multiplyRR); + bo[index(c, lir::Multiply, C, R)] = CAST2(multiplyCR); + + bo[index(c, lir::Divide, R, R)] = CAST2(divideRR); + + bo[index(c, lir::FloatMultiply, R, R)] = CAST2(floatMultiplyRR); + bo[index(c, lir::FloatMultiply, M, R)] = CAST2(floatMultiplyMR); + + bo[index(c, lir::FloatDivide, R, R)] = CAST2(floatDivideRR); + bo[index(c, lir::FloatDivide, M, R)] = CAST2(floatDivideMR); + + bo[index(c, lir::Remainder, R, R)] = CAST2(remainderRR); + + bo[index(c, lir::ShiftLeft, R, R)] = CAST2(shiftLeftRR); + bo[index(c, lir::ShiftLeft, C, R)] = CAST2(shiftLeftCR); + + bo[index(c, lir::ShiftRight, R, R)] = CAST2(shiftRightRR); + bo[index(c, lir::ShiftRight, C, R)] = CAST2(shiftRightCR); + + bo[index(c, lir::UnsignedShiftRight, R, R)] = CAST2(unsignedShiftRightRR); + bo[index(c, lir::UnsignedShiftRight, C, R)] = CAST2(unsignedShiftRightCR); + + bo[index(c, lir::Float2Float, R, R)] = CAST2(float2FloatRR); + bo[index(c, lir::Float2Float, M, R)] = CAST2(float2FloatMR); + + bo[index(c, lir::Float2Int, R, R)] = CAST2(float2IntRR); + bo[index(c, lir::Float2Int, M, R)] = CAST2(float2IntMR); + + bo[index(c, lir::Int2Float, R, R)] = CAST2(int2FloatRR); + bo[index(c, lir::Int2Float, M, R)] = CAST2(int2FloatMR); + + bo[index(c, lir::Absolute, R, R)] = CAST2(absoluteRR); + bo[index(c, lir::FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); + + bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR); + bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR); + bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM); + bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/multimethod.h b/src/codegen/target/x86/multimethod.h new file mode 100644 index 0000000000..dd076aeaa6 --- /dev/null +++ b/src/codegen/target/x86/multimethod.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H + +#include "avian/common.h" + +#include + +namespace avian { +namespace codegen { +namespace x86 { + +class ArchitectureContext; + +unsigned index(ArchitectureContext*, lir::BinaryOperation operation, + lir::OperandType operand1, + lir::OperandType operand2); + +unsigned index(ArchitectureContext* c UNUSED, lir::TernaryOperation operation, + lir::OperandType operand1, lir::OperandType operand2); + +unsigned branchIndex(ArchitectureContext* c UNUSED, lir::OperandType operand1, + lir::OperandType operand2); + +void populateTables(ArchitectureContext* c); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_MULTIMETHOD_H diff --git a/src/codegen/target/x86/operations.cpp b/src/codegen/target/x86/operations.cpp new file mode 100644 index 0000000000..6e7c337891 --- /dev/null +++ b/src/codegen/target/x86/operations.cpp @@ -0,0 +1,1557 @@ +/* Copyright (c) 2008-2012, 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 "avian/target.h" +#include "avian/alloc-vector.h" +#include "avian/allocator.h" +#include "avian/zone.h" + +#include + +#include +#include + +#include "context.h" +#include "encode.h" +#include "registers.h" +#include "detect.h" +#include "operations.h" +#include "padding.h" +#include "fixup.h" + +using namespace avian::util; + +namespace avian { +namespace codegen { +namespace x86 { + +void return_(Context* c) { + opcode(c, 0xc3); +} + +void trap(Context* c) { + opcode(c, 0xcc); +} + +void ignore(Context*) { } + +void storeLoadBarrier(Context* c) { + if (useSSE(c->ac)) { + // mfence: + c->code.append(0x0f); + c->code.append(0xae); + c->code.append(0xf0); + } else { + // lock addq $0x0,(%rsp): + c->code.append(0xf0); + if (vm::TargetBytesPerWord == 8) { + c->code.append(0x48); + } + c->code.append(0x83); + c->code.append(0x04); + c->code.append(0x24); + c->code.append(0x00); + } +} + +void callC(Context* c, unsigned size UNUSED, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + unconditional(c, 0xe8, a); +} + +void longCallC(Context* c, unsigned size, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + lir::Register r(LongJumpRegister); + moveCR2(c, size, a, size, &r, 11); + callR(c, size, &r); + } else { + callC(c, size, a); + } +} + +void jumpR(Context* c, unsigned size UNUSED, lir::Register* a) { + assert(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff, 0xe0 + regCode(a)); +} + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + unconditional(c, 0xe9, a); +} + +void jumpM(Context* c, unsigned size UNUSED, lir::Memory* a) { + assert(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rsp, a->scale, a->index, a->base, a->offset); +} + +void longJumpC(Context* c, unsigned size, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + lir::Register r(LongJumpRegister); + moveCR2(c, size, a, size, &r, 11); + jumpR(c, size, &r); + } else { + jumpC(c, size, a); + } +} + +void callR(Context* c, unsigned size UNUSED, lir::Register* a) { + assert(c, size == vm::TargetBytesPerWord); + + // maybeRex.W has no meaning here so we disable it + maybeRex(c, 4, a); + opcode(c, 0xff, 0xd0 + regCode(a)); +} + +void callM(Context* c, unsigned size UNUSED, lir::Memory* a) { + assert(c, size == vm::TargetBytesPerWord); + + maybeRex(c, 4, a); + opcode(c, 0xff); + modrmSibImm(c, rdx, a->scale, a->index, a->base, a->offset); +} + +void alignedCallC(Context* c, unsigned size, lir::Constant* a) { + new(c->zone) AlignmentPadding(c, 1, 4); + callC(c, size, a); +} + +void alignedLongCallC(Context* c, unsigned size, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + new (c->zone) AlignmentPadding(c, 2, 8); + longCallC(c, size, a); + } else { + alignedCallC(c, size, a); + } +} + +void alignedJumpC(Context* c, unsigned size, lir::Constant* a) { + new (c->zone) AlignmentPadding(c, 1, 4); + jumpC(c, size, a); +} + +void alignedLongJumpC(Context* c, unsigned size, lir::Constant* a) { + assert(c, size == vm::TargetBytesPerWord); + + if (vm::TargetBytesPerWord == 8) { + new (c->zone) AlignmentPadding(c, 2, 8); + longJumpC(c, size, a); + } else { + alignedJumpC(c, size, a); + } +} + +void pushR(Context* c, unsigned size, lir::Register* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + lir::Register ah(a->high); + + pushR(c, 4, &ah); + pushR(c, 4, a); + } else { + maybeRex(c, 4, a); + opcode(c, 0x50 + regCode(a)); + } +} + +void popR(Context* c, unsigned size, lir::Register* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + lir::Register ah(a->high); + + popR(c, 4, a); + popR(c, 4, &ah); + } else { + maybeRex(c, 4, a); + opcode(c, 0x58 + regCode(a)); + if (vm::TargetBytesPerWord == 8 and size == 4) { + moveRR(c, 4, a, 8, a); + } + } +} + +void negateR(Context* c, unsigned size, lir::Register* a) +{ + if (vm::TargetBytesPerWord == 4 and size == 8) { + assert(c, a->low == rax and a->high == rdx); + + ResolvedPromise zeroPromise(0); + lir::Constant zero(&zeroPromise); + + lir::Register ah(a->high); + + negateR(c, 4, a); + addCarryCR(c, 4, &zero, &ah); + negateR(c, 4, &ah); + } else { + maybeRex(c, size, a); + opcode(c, 0xf7, 0xd8 + regCode(a)); + } +} + +void negateRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED) +{ + assert(c, aSize == bSize); + + negateR(c, aSize, a); +} + +void moveCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + if (isFloatReg(b)) { + sseMoveCR(c, aSize, a, bSize, b); + } else { + moveCR2(c, aSize, a, bSize, b, 0); + } +} + +void moveZCR(Context* c, unsigned aSize UNUSED, lir::Constant* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, not isFloatReg(b)); + assert(c, aSize == 2); + assert(c, bSize == vm::TargetBytesPerWord); + assert(c, a->value->resolved()); + + maybeRex(c, vm::TargetBytesPerWord, b); + opcode(c, 0xb8 + regCode(b)); + c->code.appendTargetAddress(static_cast(a->value->value())); +} + +void swapRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + assert(c, aSize == vm::TargetBytesPerWord); + + alwaysRex(c, aSize, a, b); + opcode(c, 0x87); + modrm(c, 0xc0, b, a); +} + +void moveRR(Context* c, unsigned aSize, lir::Register* a, + UNUSED unsigned bSize, lir::Register* b) +{ + if (isFloatReg(a) or isFloatReg(b)) { + sseMoveRR(c, aSize, a, bSize, b); + return; + } + + if (vm::TargetBytesPerWord == 4 and aSize == 8 and bSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + if (a->high == b->low) { + if (a->low == b->high) { + swapRR(c, 4, a, 4, b); + } else { + moveRR(c, 4, &ah, 4, &bh); + moveRR(c, 4, a, 4, b); + } + } else { + moveRR(c, 4, a, 4, b); + moveRR(c, 4, &ah, 4, &bh); + } + } else { + switch (aSize) { + case 1: + if (vm::TargetBytesPerWord == 4 and a->low > rbx) { + assert(c, b->low <= rbx); + + moveRR(c, vm::TargetBytesPerWord, a, vm::TargetBytesPerWord, b); + moveRR(c, 1, b, vm::TargetBytesPerWord, b); + } else { + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbe); + modrm(c, 0xc0, a, b); + } + break; + + case 2: + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xbf); + modrm(c, 0xc0, a, b); + break; + + case 4: + if (bSize == 8) { + if (vm::TargetBytesPerWord == 8) { + alwaysRex(c, bSize, b, a); + opcode(c, 0x63); + modrm(c, 0xc0, a, b); + } else { + if (a->low == rax and b->low == rax and b->high == rdx) { + opcode(c, 0x99); //cdq + } else { + assert(c, b->low == rax and b->high == rdx); + + moveRR(c, 4, a, 4, b); + moveRR(c, 4, b, 8, b); + } + } + } else { + if (a->low != b->low) { + alwaysRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); + } + } + break; + + case 8: + if (a->low != b->low){ + maybeRex(c, aSize, a, b); + opcode(c, 0x89); + modrm(c, 0xc0, b, a); + } + break; + } + } +} + +void moveMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b) +{ + if (isFloatReg(b)) { + sseMoveMR(c, aSize, a, bSize, b); + return; + } + + switch (aSize) { + case 1: + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbe); + modrmSibImm(c, b, a); + break; + + case 2: + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xbf); + modrmSibImm(c, b, a); + break; + + case 4: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, b, a); + opcode(c, 0x63); + modrmSibImm(c, b, a); + } else { + if (bSize == 8) { + assert(c, b->low == rax and b->high == rdx); + + moveMR(c, 4, a, 4, b); + moveRR(c, 4, b, 8, b); + } else { + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); + } + } + break; + + case 8: + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::Memory ah(a->base, a->offset + 4, a->index, a->scale); + lir::Register bh(b->high); + + moveMR(c, 4, a, 4, b); + moveMR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, bSize, b, a); + opcode(c, 0x8b); + modrmSibImm(c, b, a); + } + break; + + default: abort(c); + } +} + +void moveRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Memory* b) +{ + assert(c, aSize == bSize); + + if (isFloatReg(a)) { + sseMoveRM(c, aSize, a, bSize, b); + return; + } + + switch (aSize) { + case 1: + maybeRex(c, bSize, a, b); + opcode(c, 0x88); + modrmSibImm(c, a, b); + break; + + case 2: + opcode(c, 0x66); + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + break; + + case 4: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + break; + } else { + opcode(c, 0x89); + modrmSibImm(c, a, b); + } + break; + + case 8: + if (vm::TargetBytesPerWord == 8) { + maybeRex(c, bSize, a, b); + opcode(c, 0x89); + modrmSibImm(c, a, b); + } else { + lir::Register ah(a->high); + lir::Memory bh(b->base, b->offset + 4, b->index, b->scale); + + moveRM(c, 4, a, 4, b); + moveRM(c, 4, &ah, 4, &bh); + } + break; + + default: abort(c); + } +} + +void moveAR(Context* c, unsigned aSize, lir::Address* a, + unsigned bSize, lir::Register* b) +{ + assert(c, vm::TargetBytesPerWord == 8 or (aSize == 4 and bSize == 4)); + + lir::Constant constant(a->address); + lir::Memory memory(b->low, 0, -1, 0); + + moveCR(c, aSize, &constant, bSize, b); + moveMR(c, bSize, &memory, bSize, b); +} + +void moveCM(Context* c, unsigned aSize UNUSED, lir::Constant* a, + unsigned bSize, lir::Memory* b) +{ + switch (bSize) { + case 1: + maybeRex(c, bSize, b); + opcode(c, 0xc6); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); + c->code.append(a->value->value()); + break; + + case 2: + opcode(c, 0x66); + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); + c->code.append2(a->value->value()); + break; + + case 4: + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); + if (a->value->resolved()) { + c->code.append4(a->value->value()); + } else { + appendImmediateTask(c, a->value, offsetPromise(c), 4); + c->code.append4(0); + } + break; + + case 8: { + if (vm::TargetBytesPerWord == 8) { + if (a->value->resolved() and vm::fitsInInt32(a->value->value())) { + maybeRex(c, bSize, b); + opcode(c, 0xc7); + modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); + c->code.append4(a->value->value()); + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, 8, a, 8, &tmp); + moveRM(c, 8, &tmp, 8, b); + c->client->releaseTemporary(tmp.low); + } + } else { + lir::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF)); + lir::Constant al(shiftMaskPromise(c, a->value, 0, 0xFFFFFFFF)); + + lir::Memory bh(b->base, b->offset + 4, b->index, b->scale); + + moveCM(c, 4, &al, 4, b); + moveCM(c, 4, &ah, 4, &bh); + } + } break; + + default: abort(c); + } +} + +void moveZRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + switch (aSize) { + case 2: + alwaysRex(c, aSize, b, a); + opcode(c, 0x0f, 0xb7); + modrm(c, 0xc0, a, b); + break; + + default: abort(c); + } +} + +void moveZMR(Context* c, unsigned aSize UNUSED, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, bSize == vm::TargetBytesPerWord); + assert(c, aSize == 2); + + maybeRex(c, bSize, b, a); + opcode(c, 0x0f, 0xb7); + modrmSibImm(c, b->low, a->scale, a->index, a->base, a->offset); +} + +void addCarryRR(Context* c, unsigned size, lir::Register* a, + lir::Register* b) +{ + assert(c, vm::TargetBytesPerWord == 8 or size == 4); + + maybeRex(c, size, a, b); + opcode(c, 0x11); + modrm(c, 0xc0, b, a); +} + +void addRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + addRR(c, 4, a, 4, b); + addCarryRR(c, 4, &ah, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x01); + modrm(c, 0xc0, b, a); + } +} + +void addCarryCR(Context* c, unsigned size, lir::Constant* a, + lir::Register* b) +{ + + int64_t v = a->value->value(); + maybeRex(c, size, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xd0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xd0 + regCode(b)); + c->code.append4(v); + } +} + +void addCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + addCR(c, 4, &al, 4, b); + addCarryCR(c, 4, &ah, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xc0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xc0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + addRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void subtractBorrowCR(Context* c, unsigned size UNUSED, lir::Constant* a, + lir::Register* b) +{ + assert(c, vm::TargetBytesPerWord == 8 or size == 4); + + int64_t v = a->value->value(); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xd8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xd8 + regCode(b)); + c->code.append4(v); + } +} + +void subtractCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + subtractCR(c, 4, &al, 4, b); + subtractBorrowCR(c, 4, &ah, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xe8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xe8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + subtractRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void subtractBorrowRR(Context* c, unsigned size, lir::Register* a, + lir::Register* b) +{ + assert(c, vm::TargetBytesPerWord == 8 or size == 4); + + maybeRex(c, size, a, b); + opcode(c, 0x19); + modrm(c, 0xc0, b, a); +} + +void subtractRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + subtractRR(c, 4, a, 4, b); + subtractBorrowRR(c, 4, &ah, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x29); + modrm(c, 0xc0, b, a); + } +} + +void andRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + andRR(c, 4, a, 4, b); + andRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x21); + modrm(c, 0xc0, b, a); + } +} + +void andCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + int64_t v = a->value->value(); + + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + andCR(c, 4, &al, 4, b); + andCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xe0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xe0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + andRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } +} + +void orRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + orRR(c, 4, a, 4, b); + orRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x09); + modrm(c, 0xc0, b, a); + } +} + +void orCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + orCR(c, 4, &al, 4, b); + orCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xc8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xc8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + orRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void xorRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + xorRR(c, 4, a, 4, b); + xorRR(c, 4, &ah, 4, &bh); + } else { + maybeRex(c, aSize, a, b); + opcode(c, 0x31); + modrm(c, 0xc0, b, a); + } +} + +void xorCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + int64_t v = a->value->value(); + if (v) { + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + lir::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + lir::Constant al(&low); + + lir::Register bh(b->high); + + xorCR(c, 4, &al, 4, b); + xorCR(c, 4, &ah, 4, &bh); + } else { + if (vm::fitsInInt32(v)) { + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xf0 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xf0 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + xorRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void multiplyRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + assert(c, b->high == rdx); + assert(c, b->low != rax); + assert(c, a->low != rax); + assert(c, a->high != rax); + + c->client->save(rax); + + lir::Register axdx(rax, rdx); + lir::Register ah(a->high); + lir::Register bh(b->high); + + lir::Register tmp(-1); + lir::Register* scratch; + if (a->low == b->low) { + tmp.low = c->client->acquireTemporary + (GeneralRegisterMask & ~(1 << rax)); + scratch = &tmp; + moveRR(c, 4, b, 4, scratch); + } else { + scratch = b; + } + + moveRR(c, 4, b, 4, &axdx); + multiplyRR(c, 4, &ah, 4, scratch); + multiplyRR(c, 4, a, 4, &bh); + addRR(c, 4, &bh, 4, scratch); + + // mul a->low,%eax%edx + opcode(c, 0xf7, 0xe0 + a->low); + + addRR(c, 4, scratch, 4, &bh); + moveRR(c, 4, &axdx, 4, b); + + if (tmp.low != -1) { + c->client->releaseTemporary(tmp.low); + } + } else { + maybeRex(c, aSize, b, a); + opcode(c, 0x0f, 0xaf); + modrm(c, 0xc0, a, b); + } +} + +void compareRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + assert(c, aSize <= vm::TargetBytesPerWord); + + maybeRex(c, aSize, a, b); + opcode(c, 0x39); + modrm(c, 0xc0, b, a); +} + +void compareCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + assert(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (a->value->resolved() and vm::fitsInInt32(a->value->value())) { + int64_t v = a->value->value(); + maybeRex(c, aSize, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x83, 0xf8 + regCode(b)); + c->code.append(v); + } else { + opcode(c, 0x81, 0xf8 + regCode(b)); + c->code.append4(v); + } + } else { + lir::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + compareRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +void compareRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Memory* b) +{ + assert(c, aSize == bSize); + assert(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (vm::TargetBytesPerWord == 8 and aSize == 4) { + moveRR(c, 4, a, 8, a); + } + maybeRex(c, bSize, a, b); + opcode(c, 0x39); + modrmSibImm(c, a, b); +} + +void compareCM(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b) +{ + assert(c, aSize == bSize); + assert(c, vm::TargetBytesPerWord == 8 or aSize == 4); + + if (a->value->resolved()) { + int64_t v = a->value->value(); + maybeRex(c, aSize, b); + opcode(c, vm::fitsInInt8(v) ? 0x83 : 0x81); + modrmSibImm(c, rdi, b->scale, b->index, b->base, b->offset); + + if (vm::fitsInInt8(v)) { + c->code.append(v); + } else if (vm::fitsInInt32(v)) { + c->code.append4(v); + } else { + abort(c); + } + } else { + lir::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, bSize, &tmp); + compareRM(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + +void compareFloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + if (aSize == 8) { + opcode(c, 0x66); + } + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x2e); + modrm(c, 0xc0, a, b); +} + +void branchLong(Context* c, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compare) +{ + compare(c, 4, ah, 4, bh); + + unsigned next = 0; + + switch (op) { + case lir::JumpIfEqual: + opcode(c, 0x75); // jne + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x84, target); // je + break; + + case lir::JumpIfNotEqual: + conditional(c, 0x85, target); // jne + + compare(c, 4, al, 4, bl); + conditional(c, 0x85, target); // jne + break; + + case lir::JumpIfLess: + conditional(c, 0x8c, target); // jl + + opcode(c, 0x7f); // jg + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x82, target); // jb + break; + + case lir::JumpIfGreater: + conditional(c, 0x8f, target); // jg + + opcode(c, 0x7c); // jl + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x87, target); // ja + break; + + case lir::JumpIfLessOrEqual: + conditional(c, 0x8c, target); // jl + + opcode(c, 0x7f); // jg + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x86, target); // jbe + break; + + case lir::JumpIfGreaterOrEqual: + conditional(c, 0x8f, target); // jg + + opcode(c, 0x7c); // jl + next = c->code.length(); + c->code.append(0); + + compare(c, 4, al, 4, bl); + conditional(c, 0x83, target); // jae + break; + + default: + abort(c); + } + + if (next) { + int8_t nextOffset = c->code.length() - next - 1; + c->code.set(next, &nextOffset, 1); + } +} + +void branchRR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target) +{ + if (isFloatBranch(op)) { + compareFloatRR(c, size, a, size, b); + branchFloat(c, op, target); + } else if (size > vm::TargetBytesPerWord) { + lir::Register ah(a->high); + lir::Register bh(b->high); + + branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR)); + } else { + compareRR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchCR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target) +{ + assert(c, not isFloatBranch(op)); + + if (size > vm::TargetBytesPerWord) { + int64_t v = a->value->value(); + + ResolvedPromise low(v & ~static_cast(0)); + lir::Constant al(&low); + + ResolvedPromise high((v >> 32) & ~static_cast(0)); + lir::Constant ah(&high); + + lir::Register bh(b->high); + + branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR)); + } else { + compareCR(c, size, a, size, b); + branch(c, op, target); + } +} + +void branchRM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target) +{ + assert(c, not isFloatBranch(op)); + assert(c, size <= vm::TargetBytesPerWord); + + compareRM(c, size, a, size, b); + branch(c, op, target); +} + +void branchCM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target) +{ + assert(c, not isFloatBranch(op)); + assert(c, size <= vm::TargetBytesPerWord); + + compareCM(c, size, a, size, b); + branch(c, op, target); +} + +void multiplyCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + assert(c, aSize == bSize); + + if (vm::TargetBytesPerWord == 4 and aSize == 8) { + const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); + lir::Register tmp(c->client->acquireTemporary(mask), + c->client->acquireTemporary(mask)); + + moveCR(c, aSize, a, aSize, &tmp); + multiplyRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + c->client->releaseTemporary(tmp.high); + } else { + int64_t v = a->value->value(); + if (v != 1) { + if (vm::fitsInInt32(v)) { + maybeRex(c, bSize, b, b); + if (vm::fitsInInt8(v)) { + opcode(c, 0x6b); + modrm(c, 0xc0, b, b); + c->code.append(v); + } else { + opcode(c, 0x69); + modrm(c, 0xc0, b, b); + c->code.append4(v); + } + } else { + lir::Register tmp + (c->client->acquireTemporary(GeneralRegisterMask)); + moveCR(c, aSize, a, aSize, &tmp); + multiplyRR(c, aSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } + } + } +} + +void divideRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED) +{ + assert(c, aSize == bSize); + + assert(c, b->low == rax); + assert(c, a->low != rdx); + + c->client->save(rdx); + + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); +} + +void remainderRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, aSize == bSize); + + assert(c, b->low == rax); + assert(c, a->low != rdx); + + c->client->save(rdx); + + maybeRex(c, aSize, a, b); + opcode(c, 0x99); // cdq + maybeRex(c, aSize, b, a); + opcode(c, 0xf7, 0xf8 + regCode(a)); + + lir::Register dx(rdx); + moveRR(c, vm::TargetBytesPerWord, &dx, vm::TargetBytesPerWord, b); +} + +void doShift(Context* c, UNUSED void (*shift) + (Context*, unsigned, lir::Register*, unsigned, + lir::Register*), + int type, UNUSED unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + int64_t v = a->value->value(); + + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + c->client->save(rcx); + + lir::Register cx(rcx); + ResolvedPromise promise(v & 0x3F); + lir::Constant masked(&promise); + moveCR(c, 4, &masked, 4, &cx); + shift(c, aSize, &cx, bSize, b); + } else { + maybeRex(c, bSize, b); + if (v == 1) { + opcode(c, 0xd1, type + regCode(b)); + } else if (vm::fitsInInt8(v)) { + opcode(c, 0xc1, type + regCode(b)); + c->code.append(v); + } else { + abort(c); + } + } +} + +void shiftLeftRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::Register cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shld + opcode(c, 0x0f, 0xa5); + modrm(c, 0xc0, b->high, b->low); + + // shl + opcode(c, 0xd3, 0xe0 + b->low); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR(c, aSize, &constant, aSize, &cx); + + opcode(c, 0x7c); //jl + c->code.append(2 + 2); + + lir::Register bh(b->high); + moveRR(c, 4, b, 4, &bh); // 2 bytes + xorRR(c, 4, b, 4, b); // 2 bytes + } else { + assert(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe0 + regCode(b)); + } +} + +void shiftLeftCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + doShift(c, shiftLeftRR, 0xe0, aSize, a, bSize, b); +} + +void shiftRightRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::Register cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shrd + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); + + // sar + opcode(c, 0xd3, 0xf8 + b->high); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR(c, aSize, &constant, aSize, &cx); + + opcode(c, 0x7c); //jl + c->code.append(2 + 3); + + lir::Register bh(b->high); + moveRR(c, 4, &bh, 4, b); // 2 bytes + + // sar 31,high + opcode(c, 0xc1, 0xf8 + b->high); + c->code.append(31); + } else { + assert(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xf8 + regCode(b)); + } +} + +void shiftRightCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + doShift(c, shiftRightRR, 0xf8, aSize, a, bSize, b); +} + +void unsignedShiftRightRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + if (vm::TargetBytesPerWord == 4 and bSize == 8) { + lir::Register cx(rcx); + if (a->low != rcx) { + c->client->save(rcx); + ResolvedPromise promise(0x3F); + lir::Constant mask(&promise); + moveRR(c, 4, a, 4, &cx); + andCR(c, 4, &mask, 4, &cx); + } + + // shrd + opcode(c, 0x0f, 0xad); + modrm(c, 0xc0, b->low, b->high); + + // shr + opcode(c, 0xd3, 0xe8 + b->high); + + ResolvedPromise promise(32); + lir::Constant constant(&promise); + compareCR(c, aSize, &constant, aSize, &cx); + + opcode(c, 0x7c); //jl + c->code.append(2 + 2); + + lir::Register bh(b->high); + moveRR(c, 4, &bh, 4, b); // 2 bytes + xorRR(c, 4, &bh, 4, &bh); // 2 bytes + } else { + assert(c, a->low == rcx); + + maybeRex(c, bSize, a, b); + opcode(c, 0xd3, 0xe8 + regCode(b)); + } +} + +void unsignedShiftRightCR(Context* c, unsigned aSize UNUSED, lir::Constant* a, + unsigned bSize, lir::Register* b) +{ + doShift(c, unsignedShiftRightRR, 0xe8, aSize, a, bSize, b); +} + +void floatSqrtRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x51); +} + +void floatSqrtMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x51); +} + +void floatAddRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x58); +} + +void floatAddMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x58); +} + +void floatSubtractRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5c); +} + +void floatSubtractMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5c); +} + +void floatMultiplyRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x59); +} + +void floatMultiplyMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x59); +} + +void floatDivideRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5e); +} + +void floatDivideMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5e); +} + +void float2FloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatRegOp(c, aSize, a, 4, b, 0x5a); +} + +void float2FloatMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b) +{ + floatMemOp(c, aSize, a, 4, b, 0x5a); +} + +void float2IntRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + assert(c, not isFloatReg(b)); + floatRegOp(c, aSize, a, bSize, b, 0x2c); +} + +void float2IntMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b) +{ + floatMemOp(c, aSize, a, bSize, b, 0x2c); +} + +void int2FloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b) +{ + floatRegOp(c, bSize, a, aSize, b, 0x2a); +} + +void int2FloatMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b) +{ + floatMemOp(c, bSize, a, aSize, b, 0x2a); +} + +void floatNegateRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, isFloatReg(a) and isFloatReg(b)); + // unlike most of the other floating point code, this does NOT + // support doubles: + assert(c, aSize == 4); + ResolvedPromise pcon(0x80000000); + lir::Constant con(&pcon); + if (a->low == b->low) { + lir::Register tmp(c->client->acquireTemporary(FloatRegisterMask)); + moveCR(c, 4, &con, 4, &tmp); + maybeRex(c, 4, a, &tmp); + opcode(c, 0x0f, 0x57); + modrm(c, 0xc0, &tmp, a); + c->client->releaseTemporary(tmp.low); + } else { + moveCR(c, 4, &con, 4, b); + if (aSize == 8) opcode(c, 0x66); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x57); + modrm(c, 0xc0, a, b); + } +} + +void floatAbsoluteRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b) +{ + assert(c, isFloatReg(a) and isFloatReg(b)); + // unlike most of the other floating point code, this does NOT + // support doubles: + assert(c, aSize == 4); + ResolvedPromise pcon(0x7fffffff); + lir::Constant con(&pcon); + if (a->low == b->low) { + lir::Register tmp(c->client->acquireTemporary(FloatRegisterMask)); + moveCR(c, 4, &con, 4, &tmp); + maybeRex(c, 4, a, &tmp); + opcode(c, 0x0f, 0x54); + modrm(c, 0xc0, &tmp, a); + c->client->releaseTemporary(tmp.low); + } else { + moveCR(c, 4, &con, 4, b); + maybeRex(c, 4, a, b); + opcode(c, 0x0f, 0x54); + modrm(c, 0xc0, a, b); + } +} + +void absoluteRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED) +{ + assert(c, aSize == bSize and a->low == rax and b->low == rax); + lir::Register d + (c->client->acquireTemporary(static_cast(1) << rdx)); + maybeRex(c, aSize, a, b); + opcode(c, 0x99); + xorRR(c, aSize, &d, aSize, a); + subtractRR(c, aSize, &d, aSize, a); + c->client->releaseTemporary(rdx); +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/operations.h b/src/codegen/target/x86/operations.h new file mode 100644 index 0000000000..e6250ef213 --- /dev/null +++ b/src/codegen/target/x86/operations.h @@ -0,0 +1,270 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H + +#include "avian/common.h" + +#include + +#include "context.h" + +namespace avian { +namespace codegen { +namespace x86 { + +void return_(Context* c); + +void trap(Context* c); + +void ignore(Context*); + +void storeLoadBarrier(Context* c); + +void callC(Context* c, unsigned size UNUSED, lir::Constant* a); + +void longCallC(Context* c, unsigned size, lir::Constant* a); + +void jumpR(Context* c, unsigned size UNUSED, lir::Register* a); + +void jumpC(Context* c, unsigned size UNUSED, lir::Constant* a); + +void jumpM(Context* c, unsigned size UNUSED, lir::Memory* a); + +void longJumpC(Context* c, unsigned size, lir::Constant* a); + +void callR(Context* c, unsigned size UNUSED, lir::Register* a); + +void callM(Context* c, unsigned size UNUSED, lir::Memory* a); + +void alignedCallC(Context* c, unsigned size, lir::Constant* a); + +void alignedLongCallC(Context* c, unsigned size, lir::Constant* a); + +void alignedJumpC(Context* c, unsigned size, lir::Constant* a); + +void alignedLongJumpC(Context* c, unsigned size, lir::Constant* a); + +void pushR(Context* c, unsigned size, lir::Register* a); + +void popR(Context* c, unsigned size, lir::Register* a); + +void negateR(Context* c, unsigned size, lir::Register* a); + +void negateRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED); + +void moveCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void moveZCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void swapRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void moveRR(Context* c, unsigned aSize, lir::Register* a, + UNUSED unsigned bSize, lir::Register* b); + +void moveMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b); + +void moveRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Memory* b); + +void moveAR(Context* c, unsigned aSize, lir::Address* a, + unsigned bSize, lir::Register* b); + +void moveCM(Context* c, unsigned aSize UNUSED, lir::Constant* a, + unsigned bSize, lir::Memory* b); + +void moveZRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void moveZMR(Context* c, unsigned aSize UNUSED, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void addCarryRR(Context* c, unsigned size, lir::Register* a, + lir::Register* b); + +void addRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void addCarryCR(Context* c, unsigned size, lir::Constant* a, + lir::Register* b); + +void addCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void subtractBorrowCR(Context* c, unsigned size UNUSED, lir::Constant* a, + lir::Register* b); + +void subtractCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void subtractBorrowRR(Context* c, unsigned size, lir::Register* a, + lir::Register* b); + +void subtractRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void andRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void andCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void orRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void orCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void xorRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void xorCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void multiplyRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void compareRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void compareCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void compareRM(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Memory* b); + +void compareCM(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Memory* b); + +void compareFloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void branchLong(Context* c, lir::TernaryOperation op, lir::Operand* al, + lir::Operand* ah, lir::Operand* bl, + lir::Operand* bh, lir::Constant* target, + BinaryOperationType compare); + +void branchRR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Register* b, + lir::Constant* target); + +void branchCR(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Register* b, + lir::Constant* target); + +void branchRM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Register* a, lir::Memory* b, + lir::Constant* target); + +void branchCM(Context* c, lir::TernaryOperation op, unsigned size, + lir::Constant* a, lir::Memory* b, + lir::Constant* target); + +void multiplyCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void divideRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED); + +void remainderRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void doShift(Context* c, UNUSED void (*shift) + (Context*, unsigned, lir::Register*, unsigned, + lir::Register*), + int type, UNUSED unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void shiftLeftRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void shiftLeftCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void shiftRightRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void shiftRightCR(Context* c, unsigned aSize, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void unsignedShiftRightRR(Context* c, UNUSED unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void unsignedShiftRightCR(Context* c, unsigned aSize UNUSED, lir::Constant* a, + unsigned bSize, lir::Register* b); + +void floatSqrtRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatSqrtMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatAddRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatAddMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatSubtractRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatSubtractMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatMultiplyRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatMultiplyMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatDivideRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatDivideMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void float2FloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void float2FloatMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize UNUSED, lir::Register* b); + +void float2IntRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void float2IntMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b); + +void int2FloatRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize, lir::Register* b); + +void int2FloatMR(Context* c, unsigned aSize, lir::Memory* a, + unsigned bSize, lir::Register* b); + +void floatNegateRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void floatAbsoluteRR(Context* c, unsigned aSize UNUSED, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b); + +void absoluteRR(Context* c, unsigned aSize, lir::Register* a, + unsigned bSize UNUSED, lir::Register* b UNUSED); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_OPERATIONS_H diff --git a/src/codegen/target/x86/padding.cpp b/src/codegen/target/x86/padding.cpp new file mode 100644 index 0000000000..8b2f8e90b0 --- /dev/null +++ b/src/codegen/target/x86/padding.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2008-2012, 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 "avian/alloc-vector.h" + +#include "context.h" +#include "padding.h" +#include "block.h" + +namespace avian { +namespace codegen { +namespace x86 { + +AlignmentPadding::AlignmentPadding(Context* c, unsigned instructionOffset, unsigned alignment): + offset(c->code.length()), + instructionOffset(instructionOffset), + alignment(alignment), + next(0), + padding(-1) +{ + if (c->lastBlock->firstPadding) { + c->lastBlock->lastPadding->next = this; + } else { + c->lastBlock->firstPadding = this; + } + c->lastBlock->lastPadding = this; +} + +unsigned +padding(AlignmentPadding* p, unsigned start, unsigned offset, + AlignmentPadding* limit) +{ + unsigned padding = 0; + if (limit) { + if (limit->padding == -1) { + for (; p; p = p->next) { + if (p->padding == -1) { + unsigned index = p->offset - offset; + while ((start + index + padding + p->instructionOffset) + % p->alignment) + { + ++ padding; + } + + p->padding = padding; + + if (p == limit) break; + } else { + padding = p->padding; + } + } + } else { + padding = limit->padding; + } + } + return padding; +} + +} // namespace x86 +} // namespace codegen +} // namespace avian diff --git a/src/codegen/target/x86/padding.h b/src/codegen/target/x86/padding.h new file mode 100644 index 0000000000..ef9f834f18 --- /dev/null +++ b/src/codegen/target/x86/padding.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H + +namespace avian { +namespace codegen { +namespace x86 { + +class Context; + +class AlignmentPadding { + public: + AlignmentPadding(Context* c, unsigned instructionOffset, unsigned alignment); + + unsigned offset; + unsigned instructionOffset; + unsigned alignment; + AlignmentPadding* next; + int padding; +}; + +unsigned +padding(AlignmentPadding* p, unsigned start, unsigned offset, + AlignmentPadding* limit); + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_PADDING_H diff --git a/src/codegen/target/x86/registers.h b/src/codegen/target/x86/registers.h new file mode 100644 index 0000000000..d5f325bca0 --- /dev/null +++ b/src/codegen/target/x86/registers.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2012, 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. */ + +#ifndef AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H +#define AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H + +namespace avian { +namespace codegen { +namespace x86 { + +enum { + rax = 0, + rcx = 1, + rdx = 2, + rbx = 3, + rsp = 4, + rbp = 5, + rsi = 6, + rdi = 7, + r8 = 8, + r9 = 9, + r10 = 10, + r11 = 11, + r12 = 12, + r13 = 13, + r14 = 14, + r15 = 15, +}; + +enum { + xmm0 = r15 + 1, + xmm1, + xmm2, + xmm3, + xmm4, + xmm5, + xmm6, + xmm7, + xmm8, + xmm9, + xmm10, + xmm11, + xmm12, + xmm13, + xmm14, + xmm15, +}; + +const int LongJumpRegister = r10; + +const unsigned GeneralRegisterMask = vm::TargetBytesPerWord == 4 ? 0x000000ff : 0x0000ffff; + +const unsigned FloatRegisterMask = vm::TargetBytesPerWord == 4 ? 0x00ff0000 : 0xffff0000; + + +} // namespace x86 +} // namespace codegen +} // namespace avian + +#endif // AVIAN_CODEGEN_ASSEMBLER_X86_REGISTERS_H diff --git a/src/codegen/targets.cpp b/src/codegen/targets.cpp new file mode 100644 index 0000000000..511261087e --- /dev/null +++ b/src/codegen/targets.cpp @@ -0,0 +1,40 @@ +/* Copyright (c) 2008-2012, 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 "avian/common.h" + +#include + +#include "avian/environment.h" + +namespace avian { +namespace codegen { + +Architecture* makeArchitectureNative(vm::System* system, bool useNativeFeatures UNUSED) { +#ifndef AVIAN_TARGET_ARCH + #error "Must specify native target!" +#endif + +#if AVIAN_TARGET_ARCH == AVIAN_ARCH_UNKNOWN + system->abort(); + return 0; +#elif (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86) || (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86_64) + return makeArchitectureX86(system, useNativeFeatures); +#elif AVIAN_TARGET_ARCH == AVIAN_ARCH_ARM + return makeArchitectureArm(system, useNativeFeatures); +#elif AVIAN_TARGET_ARCH == AVIAN_ARCH_POWERPC + return makeArchitecturePowerpc(system, useNativeFeatures); +#else + #error "Unsupported codegen target" +#endif +} + +} // namespace codegen +} // namespace avian diff --git a/src/compile-arm.S b/src/compile-arm.S index 427378c1f8..be63018e3e 100644 --- a/src/compile-arm.S +++ b/src/compile-arm.S @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" -#include "target-fields.h" +#include "avian/types.h" +#include "avian/target-fields.h" .text @@ -75,7 +75,12 @@ LOCAL(vmInvoke_argumentTest): mov r8, r0 // load and call function address +#if defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) + mov lr, pc + bx r1 +#else blx r1 +#endif .globl GLOBAL(vmInvoke_returnAddress) .align 2 @@ -123,11 +128,18 @@ LOCAL(vmInvoke_continuationTest): ble LOCAL(vmInvoke_continuationLoop) ldr r7,[r5,#CONTINUATION_RETURN_ADDRESS_OFFSET] +#ifdef __APPLE__ + movw r11, :lower16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmInvoke_getAddress)+8)) + movt r11, :upper16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmInvoke_getAddress)+8)) +LOCAL(vmInvoke_getAddress): + add r11, pc, r11 +#else // not __APPLE__ ldr r10,LOCAL(vmInvoke_returnAddress_word) ldr r11,LOCAL(vmInvoke_getAddress_word) LOCAL(vmInvoke_getAddress): add r11,pc,r11 ldr r11,[r11,r10] +#endif // not __APPLE__ str r11,[sp,r7] ldr r7,[r5,#CONTINUATION_NEXT] @@ -210,23 +222,34 @@ LOCAL(vmJumpAndInvoke_argumentTest): mov sp,r2 // set return address to vmInvoke_returnAddress +#ifdef __APPLE__ + movw r11, :lower16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmJumpAndInvoke_getAddress)+8)) + movt r11, :upper16:(GLOBAL(vmInvoke_returnAddress)-(LOCAL(vmJumpAndInvoke_getAddress)+8)) +LOCAL(vmJumpAndInvoke_getAddress): + add r11, pc, r11 +#else // not __APPLE__ + ldr r10,LOCAL(vmInvoke_returnAddress_word) ldr r11,LOCAL(vmJumpAndInvoke_getAddress_word) LOCAL(vmJumpAndInvoke_getAddress): add r11,pc,r11 +#endif // not __APPLE__ ldr lr,[r11,r10] bx r1 +#ifndef __APPLE__ LOCAL(vmInvoke_returnAddress_word): .word GLOBAL(vmInvoke_returnAddress)(GOT) LOCAL(vmInvoke_getAddress_word): .word _GLOBAL_OFFSET_TABLE_-(LOCAL(vmInvoke_getAddress)+8) LOCAL(vmJumpAndInvoke_getAddress_word): .word _GLOBAL_OFFSET_TABLE_-(LOCAL(vmJumpAndInvoke_getAddress)+8) +#endif // not __APPLE__ #else // not AVIAN_CONTINUATIONS // vmJumpAndInvoke should only be called when continuations are - // enabled - bkpt + // enabled, so we force a crash if we reach here: + mov r1,#0 + ldr r1,[r1] #endif // not AVIAN_CONTINUATIONS diff --git a/src/compile-arm.masm b/src/compile-arm.masm new file mode 100644 index 0000000000..a9c1e59cef --- /dev/null +++ b/src/compile-arm.masm @@ -0,0 +1,252 @@ +; Copyright (c) 2008-2011, 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. +; +; ORIGIN: https://github.com/gkvas/avian/tree/wince + +; types.inc +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +; target-fields.inc +;TARGET_BYTES_PER_WORD = 4 + +TARGET_THREAD_EXCEPTION equ 44 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2164 +TARGET_THREAD_EXCEPTIONOFFSET equ 2168 +TARGET_THREAD_EXCEPTIONHANDLER equ 2172 + +TARGET_THREAD_IP equ 2144 +TARGET_THREAD_STACK equ 2148 +TARGET_THREAD_NEWSTACK equ 2152 +TARGET_THREAD_SCRATCH equ 2156 +TARGET_THREAD_CONTINUATION equ 2160 +TARGET_THREAD_TAILADDRESS equ 2176 +TARGET_THREAD_VIRTUALCALLTARGET equ 2180 +TARGET_THREAD_VIRTUALCALLINDEX equ 2184 +TARGET_THREAD_HEAPIMAGE equ 2188 +TARGET_THREAD_CODEIMAGE equ 2192 +TARGET_THREAD_THUNKTABLE equ 2196 +TARGET_THREAD_STACKLIMIT equ 2220 + + AREA text, CODE, ARM + +BYTES_PER_WORD equ 4 + +CONTINUATION_NEXT equ 4 +CONTINUATION_ADDRESS equ 16 +CONTINUATION_RETURN_ADDRESS_OFFSET equ 20 +CONTINUATION_FRAME_POINTER_OFFSET equ 24 +CONTINUATION_LENGTH equ 28 +CONTINUATION_BODY equ 32 + + EXPORT vmInvoke +vmInvoke + + ; arguments + ; r0 : thread + ; r1 : function + ; r2 : arguments + ; r3 : argumentFootprint + ; [sp, #0] : frameSize (not used) + ; [sp, #4] : returnType + + + ; save all non-volatile registers + stmfd sp!, {r4-r11, lr} + + ; save return type + ldr r4, [sp, #4] + str r4, [sp, #-4]! + + str sp, [r0, #TARGET_THREAD_SCRATCH] + + ; align stack, if necessary + eor r4, sp, r3 + tst r4, #4 + subne sp, sp, #4 + + ; copy arguments into place + sub sp, sp, r3 + mov r4, #0 + b vmInvoke_argumentTest + +vmInvoke_argumentLoop + ldr r5, [r2, r4] + str r5, [sp, r4] + add r4, r4, #BYTES_PER_WORD + +vmInvoke_argumentTest + cmp r4, r3 + blt vmInvoke_argumentLoop + + ; we use r8 to hold the thread pointer, by convention + mov r8, r0 + + ; load and call function address + blx r1 + + EXPORT vmInvoke_returnAddress +vmInvoke_returnAddress + ; restore stack pointer + ldr sp, [r8, #TARGET_THREAD_SCRATCH] + + ; clear MyThread::stack to avoid confusing another thread calling + ; java.lang.Thread.getStackTrace on this one. See + ; MyProcess::getStackTrace in compile.cpp for details on how we get + ; a reliable stack trace from a thread that might be interrupted at + ; any point in its execution. + mov r5, #0 + str r5, [r8, #TARGET_THREAD_STACK] + + EXPORT vmInvoke_safeStack +vmInvoke_safeStack + +;if AVIAN_CONTINUATIONS +; ; call the next continuation, if any +; ldr r5,[r8,#TARGET_THREAD_CONTINUATION] +; cmp r5,#0 +; beq vmInvoke_exit) +; +; ldr r6,[r5,#CONTINUATION_LENGTH] +; lsl r6,r6,#2 +; neg r7,r6 +; add r7,r7,#-80 +; mov r4,sp +; str r4,[sp,r7]! +; +; add r7,r5,#CONTINUATION_BODY +; +; mov r11,#0 +; b vmInvoke_continuationTest +; +;vmInvoke_continuationLoop +; ldr r9,[r7,r11] +; str r9,[sp,r11] +; add r11,r11,#4 +; +;vmInvoke_continuationTest +; cmp r11,r6 +; ble vmInvoke_continuationLoop) +; +; ldr r7,[r5,#CONTINUATION_RETURN_ADDRESS_OFFSET] +; ldr r10,vmInvoke_returnAddress_word +; ldr r11,vmInvoke_getAddress_word +;vmInvoke_getAddress +; add r11,pc,r11 +; ldr r11,[r11,r10] +; str r11,[sp,r7] +; +; ldr r7,[r5,#CONTINUATION_NEXT] +; str r7,[r8,#TARGET_THREAD_CONTINUATION] +; +; ; call the continuation unless we're handling an exception +; ldr r7,[r8,#TARGET_THREAD_EXCEPTION] +; cmp r7,#0 +; bne vmInvoke_handleException) +; ldr r7,[r5,#CONTINUATION_ADDRESS] +; bx r7 +; +;vmInvoke_handleException +; ; we're handling an exception - call the exception handler instead +; mov r11,#0 +; str r11,[r8,#TARGET_THREAD_EXCEPTION] +; ldr r11,[r8,#TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT] +; ldr r9,[sp] +; neg r11,r11 +; str r9,[sp,r11]! +; ldr r11,[r8,#TARGET_THREAD_EXCEPTIONOFFSET] +; str r7,[sp,r11] +; +; ldr r7,[r8,#TARGET_THREAD_EXCEPTIONHANDLER] +; bx r7 +; +;vmInvoke_exit +;endif ; AVIAN_CONTINUATIONS + + mov ip, #0 + str ip, [r8, #TARGET_THREAD_STACK] + + ; restore return type + ldr ip, [sp], #4 + + ; restore callee-saved registers + ldmfd sp!, {r4-r11, lr} + +vmInvoke_return + bx lr + + EXPORT vmJumpAndInvoke +vmJumpAndInvoke +;if :DEF:AVIAN_CONTINUATIONS +; ; r0: thread +; ; r1: address +; ; r2: stack +; ; r3: argumentFootprint +; ; [sp,#0]: arguments +; ; [sp,#4]: frameSize +; +; ldr r5,[sp,#0] +; ldr r6,[sp,#4] +; +; ; allocate new frame, adding room for callee-saved registers, plus +; ; 4 bytes of padding since the calculation of frameSize assumes 4 +; ; bytes have already been allocated to save the return address, +; ; which is not true in this case +; sub r2,r2,r6 +; sub r2,r2,#84 +; +; mov r8,r0 +; +; ; copy arguments into place +; mov r6,#0 +; b vmJumpAndInvoke_argumentTest +; +;vmJumpAndInvoke_argumentLoop +; ldr r12,[r5,r6] +; str r12,[r2,r6] +; add r6,r6,#4 +; +;vmJumpAndInvoke_argumentTest +; cmp r6,r3 +; ble vmJumpAndInvoke_argumentLoop +; +; ; the arguments have been copied, so we can set the real stack +; ; pointer now +; mov sp,r2 +; +; ; set return address to vmInvoke_returnAddress +; ldr r10,vmInvoke_returnAddress_word) +; ldr r11,vmJumpAndInvoke_getAddress_word) +;vmJumpAndInvoke_getAddress +; add r11,pc,r11 +; ldr lr,[r11,r10] +; +; bx r1 +; +;vmInvoke_returnAddress_word +; .word GLOBAL(vmInvoke_returnAddress)(GOT) +;vmInvoke_getAddress_word +; .word _GLOBAL_OFFSET_TABLE_-(vmInvoke_getAddress)+8) +;vmJumpAndInvoke_getAddress_word +; .word _GLOBAL_OFFSET_TABLE_-(vmJumpAndInvoke_getAddress)+8) +; +;else ; not AVIAN_CONTINUATIONS + ; vmJumpAndInvoke should only be called when continuations are + ; enabled + bkpt 0 +;endif ; not AVIAN_CONTINUATIONS + + END \ No newline at end of file diff --git a/src/compile-powerpc.S b/src/compile-powerpc.S index 667ae40ed7..f843210526 100644 --- a/src/compile-powerpc.S +++ b/src/compile-powerpc.S @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" -#include "target-fields.h" +#include "avian/types.h" +#include "avian/target-fields.h" .text diff --git a/src/compile-x86.S b/src/compile-x86.S index c15ec2cb7d..9c4f2909d9 100644 --- a/src/compile-x86.S +++ b/src/compile-x86.S @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" -#include "target-fields.h" +#include "avian/types.h" +#include "avian/target-fields.h" #define LOCAL(x) .L##x diff --git a/src/compile-x86.masm b/src/compile-x86.masm new file mode 100644 index 0000000000..f07d799faf --- /dev/null +++ b/src/compile-x86.masm @@ -0,0 +1,173 @@ +comment # + Copyright (c) 2008-2011, 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. + + ORIGIN: https://github.com/gkvas/avian/tree/wince +# + +.586 +.MODEL FLAT, C + +comment # types.h # +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +comment # target-fields.h # +ifdef TARGET_BYTES_PER_WORD + if TARGET_BYTES_PER_WORD eq 8 + +TARGET_THREAD_EXCEPTION equ 80 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2256 +TARGET_THREAD_EXCEPTIONOFFSET equ 2264 +TARGET_THREAD_EXCEPTIONHANDLER equ 2272 + +TARGET_THREAD_IP equ 2216 +TARGET_THREAD_STACK equ 2224 +TARGET_THREAD_NEWSTACK equ 2232 +TARGET_THREAD_SCRATCH equ 2240 +TARGET_THREAD_CONTINUATION equ 2248 +TARGET_THREAD_TAILADDRESS equ 2280 +TARGET_THREAD_VIRTUALCALLTARGET equ 2288 +TARGET_THREAD_VIRTUALCALLINDEX equ 2296 +TARGET_THREAD_HEAPIMAGE equ 2304 +TARGET_THREAD_CODEIMAGE equ 2312 +TARGET_THREAD_THUNKTABLE equ 2320 +TARGET_THREAD_STACKLIMIT equ 2368 + + elseif TARGET_BYTES_PER_WORD eq 4 + +TARGET_THREAD_EXCEPTION equ 44 +TARGET_THREAD_EXCEPTIONSTACKADJUSTMENT equ 2164 +TARGET_THREAD_EXCEPTIONOFFSET equ 2168 +TARGET_THREAD_EXCEPTIONHANDLER equ 2172 + +TARGET_THREAD_IP equ 2144 +TARGET_THREAD_STACK equ 2148 +TARGET_THREAD_NEWSTACK equ 2152 +TARGET_THREAD_SCRATCH equ 2156 +TARGET_THREAD_CONTINUATION equ 2160 +TARGET_THREAD_TAILADDRESS equ 2176 +TARGET_THREAD_VIRTUALCALLTARGET equ 2180 +TARGET_THREAD_VIRTUALCALLINDEX equ 2184 +TARGET_THREAD_HEAPIMAGE equ 2188 +TARGET_THREAD_CODEIMAGE equ 2192 +TARGET_THREAD_THUNKTABLE equ 2196 +TARGET_THREAD_STACKLIMIT equ 2220 + + else + error + endif +else + error +endif + +ifdef AVIAN_USE_FRAME_POINTER + ALIGNMENT_ADJUSTMENT equ 0 +else + ALIGNMENT_ADJUSTMENT equ 12 +endif + +CALLEE_SAVED_REGISTER_FOOTPRINT equ 16 + ALIGNMENT_ADJUSTMENT + +_TEXT SEGMENT + +public C vmInvoke +vmInvoke: + push ebp + mov ebp,esp + + ; 8(%ebp): thread + ; 12(%ebp): function + ; 16(%ebp): arguments + ; 20(%ebp): argumentFootprint + ; 24(%ebp): frameSize + ; 28(%ebp): returnType + + ; allocate stack space for callee-saved registers + sub esp,offset CALLEE_SAVED_REGISTER_FOOTPRINT + + ; remember this stack position, since we won't be able to rely on + ; %rbp being restored when the call returns + mov eax,ds:dword ptr[8+ebp] + mov ds:dword ptr[TARGET_THREAD_SCRATCH+eax],esp + + mov ds:dword ptr[0+esp],ebx + mov ds:dword ptr[4+esp],esi + mov ds:dword ptr[8+esp],edi + + ; allocate stack space for arguments + sub esp,ds:dword ptr[24+ebp] + + ; we use ebx to hold the thread pointer, by convention + mov ebx,eax + + ; copy arguments into place + mov ecx,0 + mov edx,ds:dword ptr[16+ebp] + jmp LvmInvoke_argumentTest + +LvmInvoke_argumentLoop: + mov eax,ds:dword ptr[edx+ecx*1] + mov ds:dword ptr[esp+ecx*1],eax + add ecx,4 + +LvmInvoke_argumentTest: + cmp ecx,ds:dword ptr[20+ebp] + jb LvmInvoke_argumentLoop + + ; call function + call dword ptr[12+ebp] + +public vmInvoke_returnAddress +vmInvoke_returnAddress: + ; restore stack pointer + mov esp,ds:dword ptr[TARGET_THREAD_SCRATCH+ebx] + + ; clear MyThread::stack to avoid confusing another thread calling + ; java.lang.Thread.getStackTrace on this one. See + ; MyProcess::getStackTrace in compile.cpp for details on how we get + ; a reliable stack trace from a thread that might be interrupted at + ; any point in its execution. + mov ds:dword ptr[TARGET_THREAD_STACK+ebx],0 + +public vmInvoke_safeStack +vmInvoke_safeStack: + + ; restore callee-saved registers + mov ebx,ds:dword ptr[0+esp] + mov esi,ds:dword ptr[4+esp] + mov edi,ds:dword ptr[8+esp] + + add esp,offset CALLEE_SAVED_REGISTER_FOOTPRINT + + mov ecx,ds:dword ptr[28+esp] + + pop ebp + ret + +LgetPC: + mov esi,ds:dword ptr[esp] + ret + +public vmJumpAndInvoke +vmJumpAndInvoke: + ; vmJumpAndInvoke should only be called when continuations are + ; enabled + int 3 + +_TEXT ENDS + +END diff --git a/src/compile.cpp b/src/compile.cpp index 50e2bfb9e2..d218048f40 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -8,14 +8,19 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "util.h" -#include "vector.h" -#include "process.h" -#include "assembler.h" -#include "target.h" -#include "compiler.h" -#include "arch.h" +#include "avian/machine.h" +#include "avian/util.h" +#include "avian/alloc-vector.h" +#include "avian/process.h" +#include "avian/target.h" +#include "avian/arch.h" + +#include +#include +#include +#include + +#include using namespace vm; @@ -34,6 +39,8 @@ vmJumpAndInvoke(void* thread, void* function, void* stack, unsigned argumentFootprint, uintptr_t* arguments, unsigned frameSize); +using avian::codegen::Compiler; + namespace { namespace local { @@ -262,7 +269,7 @@ class MyThread: public Thread { reference(0), arch(parent ? parent->arch - : makeArchitecture(m->system, useNativeFeatures)), + : avian::codegen::makeArchitectureNative(m->system, useNativeFeatures)), transition(0), traceContext(0), stackLimit(0), @@ -288,7 +295,7 @@ class MyThread: public Thread { void** thunkTable; CallTrace* trace; Reference* reference; - Assembler::Architecture* arch; + avian::codegen::Architecture* arch; Context* transition; TraceContext* traceContext; uintptr_t stackLimit; @@ -734,7 +741,7 @@ stackForFrame(MyThread* t, void* frame, object method) return static_cast(frame) - stackOffsetFromFrame(t, method); } -class PoolElement: public Promise { +class PoolElement: public avian::codegen::Promise { public: PoolElement(Thread* t, object target, PoolElement* next): t(t), target(target), address(0), next(next) @@ -790,7 +797,7 @@ class SubroutinePath; class SubroutineCall { public: - SubroutineCall(Subroutine* subroutine, Promise* returnAddress): + SubroutineCall(Subroutine* subroutine, avian::codegen::Promise* returnAddress): subroutine(subroutine), returnAddress(returnAddress), paths(0), @@ -801,7 +808,7 @@ class SubroutineCall { } Subroutine* subroutine; - Promise* returnAddress; + avian::codegen::Promise* returnAddress; SubroutinePath* paths; SubroutineCall* next; }; @@ -860,7 +867,7 @@ class SubroutineTrace { uintptr_t map[0]; }; -class TraceElement: public TraceHandler { +class TraceElement: public avian::codegen::TraceHandler { public: static const unsigned VirtualCall = 1 << 0; static const unsigned TailCall = 1 << 1; @@ -879,10 +886,10 @@ class TraceElement: public TraceHandler { flags(flags), watch(false) { - memset(map, 0, mapSize * BytesPerWord); + memset(map, 0xFF, mapSize * BytesPerWord); } - virtual void handleTrace(Promise* address, unsigned argumentIndex) { + virtual void handleTrace(avian::codegen::Promise* address, unsigned argumentIndex) { if (this->address == 0) { this->address = address; this->argumentIndex = argumentIndex; @@ -890,7 +897,7 @@ class TraceElement: public TraceHandler { } Context* context; - Promise* address; + avian::codegen::Promise* address; TraceElement* next; SubroutineTrace* subroutineTrace; object target; @@ -902,7 +909,7 @@ class TraceElement: public TraceHandler { uintptr_t map[0]; }; -class TraceElementPromise: public Promise { +class TraceElementPromise: public avian::codegen::Promise { public: TraceElementPromise(System* s, TraceElement* trace): s(s), trace(trace) { } @@ -940,7 +947,7 @@ frameMapSizeInBits(MyThread* t, object method) unsigned frameMapSizeInWords(MyThread* t, object method) { - return ceiling(frameMapSizeInBits(t, method), BitsPerWord); + return ceilingDivide(frameMapSizeInBits(t, method), BitsPerWord); } uint16_t* @@ -991,7 +998,7 @@ class BootContext { }; BootContext(Thread* t, object constants, object calls, - DelayedPromise* addresses, Zone* zone, OffsetResolver* resolver): + avian::codegen::DelayedPromise* addresses, Zone* zone, OffsetResolver* resolver): protector(t, this), constants(constants), calls(calls), addresses(addresses), addressSentinal(addresses), zone(zone), resolver(resolver) @@ -1000,8 +1007,8 @@ class BootContext { MyProtector protector; object constants; object calls; - DelayedPromise* addresses; - DelayedPromise* addressSentinal; + avian::codegen::DelayedPromise* addresses; + avian::codegen::DelayedPromise* addressSentinal; Zone* zone; OffsetResolver* resolver; }; @@ -1042,32 +1049,32 @@ class Context { public: MyClient(MyThread* t): t(t) { } - virtual intptr_t getThunk(UnaryOperation, unsigned) { + virtual intptr_t getThunk(avian::codegen::lir::UnaryOperation, unsigned) { abort(t); } - virtual intptr_t getThunk(BinaryOperation op, unsigned size, + virtual intptr_t getThunk(avian::codegen::lir::BinaryOperation op, unsigned size, unsigned resultSize) { if (size == 8) { switch(op) { - case Absolute: + case avian::codegen::lir::Absolute: assert(t, resultSize == 8); return local::getThunk(t, absoluteLongThunk); - case FloatNegate: + case avian::codegen::lir::FloatNegate: assert(t, resultSize == 8); return local::getThunk(t, negateDoubleThunk); - case FloatSquareRoot: + case avian::codegen::lir::FloatSquareRoot: assert(t, resultSize == 8); return local::getThunk(t, squareRootDoubleThunk); - case Float2Float: + case avian::codegen::lir::Float2Float: assert(t, resultSize == 4); return local::getThunk(t, doubleToFloatThunk); - case Float2Int: + case avian::codegen::lir::Float2Int: if (resultSize == 8) { return local::getThunk(t, doubleToLongThunk); } else { @@ -1075,7 +1082,7 @@ class Context { return local::getThunk(t, doubleToIntThunk); } - case Int2Float: + case avian::codegen::lir::Int2Float: if (resultSize == 8) { return local::getThunk(t, longToDoubleThunk); } else { @@ -1089,23 +1096,23 @@ class Context { assert(t, size == 4); switch(op) { - case Absolute: + case avian::codegen::lir::Absolute: assert(t, resultSize == 4); return local::getThunk(t, absoluteIntThunk); - case FloatNegate: + case avian::codegen::lir::FloatNegate: assert(t, resultSize == 4); return local::getThunk(t, negateFloatThunk); - case FloatAbsolute: + case avian::codegen::lir::FloatAbsolute: assert(t, resultSize == 4); return local::getThunk(t, absoluteFloatThunk); - case Float2Float: + case avian::codegen::lir::Float2Float: assert(t, resultSize == 8); return local::getThunk(t, floatToDoubleThunk); - case Float2Int: + case avian::codegen::lir::Float2Int: if (resultSize == 4) { return local::getThunk(t, floatToIntThunk); } else { @@ -1113,7 +1120,7 @@ class Context { return local::getThunk(t, floatToLongThunk); } - case Int2Float: + case avian::codegen::lir::Int2Float: if (resultSize == 4) { return local::getThunk(t, intToFloatThunk); } else { @@ -1126,48 +1133,48 @@ class Context { } } - virtual intptr_t getThunk(TernaryOperation op, unsigned size, unsigned, + virtual intptr_t getThunk(avian::codegen::lir::TernaryOperation op, unsigned size, unsigned, bool* threadParameter) { *threadParameter = false; if (size == 8) { switch (op) { - case Divide: + case avian::codegen::lir::Divide: *threadParameter = true; return local::getThunk(t, divideLongThunk); - case Remainder: + case avian::codegen::lir::Remainder: *threadParameter = true; return local::getThunk(t, moduloLongThunk); - case FloatAdd: + case avian::codegen::lir::FloatAdd: return local::getThunk(t, addDoubleThunk); - case FloatSubtract: + case avian::codegen::lir::FloatSubtract: return local::getThunk(t, subtractDoubleThunk); - case FloatMultiply: + case avian::codegen::lir::FloatMultiply: return local::getThunk(t, multiplyDoubleThunk); - case FloatDivide: + case avian::codegen::lir::FloatDivide: return local::getThunk(t, divideDoubleThunk); - case FloatRemainder: + case avian::codegen::lir::FloatRemainder: return local::getThunk(t, moduloDoubleThunk); - case JumpIfFloatEqual: - case JumpIfFloatNotEqual: - case JumpIfFloatLess: - case JumpIfFloatGreater: - case JumpIfFloatLessOrEqual: - case JumpIfFloatGreaterOrUnordered: - case JumpIfFloatGreaterOrEqualOrUnordered: + case avian::codegen::lir::JumpIfFloatEqual: + case avian::codegen::lir::JumpIfFloatNotEqual: + case avian::codegen::lir::JumpIfFloatLess: + case avian::codegen::lir::JumpIfFloatGreater: + case avian::codegen::lir::JumpIfFloatLessOrEqual: + case avian::codegen::lir::JumpIfFloatGreaterOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqualOrUnordered: return local::getThunk(t, compareDoublesGThunk); - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatLessOrUnordered: - case JumpIfFloatLessOrEqualOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqual: + case avian::codegen::lir::JumpIfFloatLessOrUnordered: + case avian::codegen::lir::JumpIfFloatLessOrEqualOrUnordered: return local::getThunk(t, compareDoublesLThunk); default: abort(t); @@ -1175,41 +1182,41 @@ class Context { } else { assert(t, size == 4); switch (op) { - case Divide: + case avian::codegen::lir::Divide: *threadParameter = true; return local::getThunk(t, divideIntThunk); - case Remainder: + case avian::codegen::lir::Remainder: *threadParameter = true; return local::getThunk(t, moduloIntThunk); - case FloatAdd: + case avian::codegen::lir::FloatAdd: return local::getThunk(t, addFloatThunk); - case FloatSubtract: + case avian::codegen::lir::FloatSubtract: return local::getThunk(t, subtractFloatThunk); - case FloatMultiply: + case avian::codegen::lir::FloatMultiply: return local::getThunk(t, multiplyFloatThunk); - case FloatDivide: + case avian::codegen::lir::FloatDivide: return local::getThunk(t, divideFloatThunk); - case FloatRemainder: + case avian::codegen::lir::FloatRemainder: return local::getThunk(t, moduloFloatThunk); - case JumpIfFloatEqual: - case JumpIfFloatNotEqual: - case JumpIfFloatLess: - case JumpIfFloatGreater: - case JumpIfFloatLessOrEqual: - case JumpIfFloatGreaterOrUnordered: - case JumpIfFloatGreaterOrEqualOrUnordered: + case avian::codegen::lir::JumpIfFloatEqual: + case avian::codegen::lir::JumpIfFloatNotEqual: + case avian::codegen::lir::JumpIfFloatLess: + case avian::codegen::lir::JumpIfFloatGreater: + case avian::codegen::lir::JumpIfFloatLessOrEqual: + case avian::codegen::lir::JumpIfFloatGreaterOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqualOrUnordered: return local::getThunk(t, compareFloatsGThunk); - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatLessOrUnordered: - case JumpIfFloatLessOrEqualOrUnordered: + case avian::codegen::lir::JumpIfFloatGreaterOrEqual: + case avian::codegen::lir::JumpIfFloatLessOrUnordered: + case avian::codegen::lir::JumpIfFloatLessOrEqualOrUnordered: return local::getThunk(t, compareFloatsLThunk); default: abort(t); @@ -1223,7 +1230,7 @@ class Context { Context(MyThread* t, BootContext* bootContext, object method): thread(t), zone(t->m->system, t->m->heap, InitialZoneCapacityInBytes), - assembler(makeAssembler(t->m->system, t->m->heap, &zone, t->arch)), + assembler(t->arch->makeAssembler(t->m->heap, &zone)), client(t), compiler(makeCompiler(t->m->system, assembler, &zone, &client)), method(method), @@ -1249,7 +1256,7 @@ class Context { Context(MyThread* t): thread(t), zone(t->m->system, t->m->heap, InitialZoneCapacityInBytes), - assembler(makeAssembler(t->m->system, t->m->heap, &zone, t->arch)), + assembler(t->arch->makeAssembler(t->m->heap, &zone)), client(t), compiler(0), method(0), @@ -1294,9 +1301,9 @@ class Context { MyThread* thread; Zone zone; - Assembler* assembler; + avian::codegen::Assembler* assembler; MyClient client; - Compiler* compiler; + avian::codegen::Compiler* compiler; object method; BootContext* bootContext; PoolElement* objectPool; @@ -1356,6 +1363,8 @@ class Frame { Object }; + typedef Compiler::Operand* Value; + Frame(Context* context, uint8_t* stackMap): context(context), t(context->thread), @@ -1388,15 +1397,19 @@ class Frame { } ~Frame() { + dispose(); + } + + void dispose() { if (level > 1) { context->eventLog.append(PopContextEvent); } } - Compiler::Operand* append(object o) { + Value append(object o) { BootContext* bc = context->bootContext; if (bc) { - Promise* p = new (bc->zone) ListenPromise(t->m->system, bc->zone); + avian::codegen::Promise* p = new (bc->zone) avian::codegen::ListenPromise(t->m->system, bc->zone); PROTECT(t, o); object pointer = makePointer(t, p); @@ -1616,34 +1629,34 @@ class Frame { set(sp - 2, saved); } - Promise* addressPromise(Promise* p) { + avian::codegen::Promise* addressPromise(avian::codegen::Promise* p) { BootContext* bc = context->bootContext; if (bc) { - bc->addresses = new(bc->zone) DelayedPromise(t->m->system, bc->zone, p, bc->addresses); + bc->addresses = new(bc->zone) avian::codegen::DelayedPromise(t->m->system, bc->zone, p, bc->addresses); return bc->addresses; } else { return p; } } - Compiler::Operand* addressOperand(Promise* p) { + Value addressOperand(avian::codegen::Promise* p) { return c->promiseConstant(p, Compiler::AddressType); } - Compiler::Operand* absoluteAddressOperand(Promise* p) { + Value absoluteAddressOperand(avian::codegen::Promise* p) { return context->bootContext ? c->add (TargetBytesPerWord, c->memory (c->register_(t->arch->thread()), Compiler::AddressType, TARGET_THREAD_CODEIMAGE), c->promiseConstant (new(&context->zone) - OffsetPromise + avian::codegen::OffsetPromise (p, - reinterpret_cast(codeAllocator(t)->base)), Compiler::AddressType)) : addressOperand(p); } - Compiler::Operand* machineIp(unsigned logicalIp) { + Value machineIp(unsigned logicalIp) { return c->promiseConstant(c->machineIp(logicalIp), Compiler::AddressType); } @@ -1667,35 +1680,33 @@ class Frame { this->ip = ip; } - void pushQuiet(unsigned footprint, Compiler::Operand* o) { + void pushQuiet(unsigned footprint, Value o) { c->push(footprint, o); } - void pushLongQuiet(Compiler::Operand* o) { + void pushLongQuiet(Value o) { pushQuiet(2, o); } - Compiler::Operand* popQuiet(unsigned footprint) { + Value popQuiet(unsigned footprint) { return c->pop(footprint); } - Compiler::Operand* popLongQuiet() { - Compiler::Operand* r = popQuiet(2); - - return r; + Value popLongQuiet() { + return popQuiet(2); } - void pushInt(Compiler::Operand* o) { + void pushInt(Value o) { pushQuiet(1, o); pushedInt(); } - void pushAddress(Compiler::Operand* o) { + void pushAddress(Value o) { pushQuiet(1, o); pushedInt(); } - void pushObject(Compiler::Operand* o) { + void pushObject(Value o) { pushQuiet(1, o); pushedObject(); } @@ -1706,7 +1717,7 @@ class Frame { pushedObject(); } - void pushLong(Compiler::Operand* o) { + void pushLong(Value o) { pushLongQuiet(o); pushedLong(); } @@ -1716,17 +1727,17 @@ class Frame { c->popped(count); } - Compiler::Operand* popInt() { + Value popInt() { poppedInt(); return popQuiet(1); } - Compiler::Operand* popLong() { + Value popLong() { poppedLong(); return popLongQuiet(); } - Compiler::Operand* popObject() { + Value popObject() { poppedObject(); return popQuiet(1); } @@ -1782,8 +1793,8 @@ class Frame { } void dupX1() { - Compiler::Operand* s0 = popQuiet(1); - Compiler::Operand* s1 = popQuiet(1); + Value s0 = popQuiet(1); + Value s1 = popQuiet(1); pushQuiet(1, s0); pushQuiet(1, s1); @@ -1793,17 +1804,17 @@ class Frame { } void dupX2() { - Compiler::Operand* s0 = popQuiet(1); + Value s0 = popQuiet(1); if (get(sp - 2) == Long) { - Compiler::Operand* s1 = popLongQuiet(); + Value s1 = popLongQuiet(); pushQuiet(1, s0); pushLongQuiet(s1); pushQuiet(1, s0); } else { - Compiler::Operand* s1 = popQuiet(1); - Compiler::Operand* s2 = popQuiet(1); + Value s1 = popQuiet(1); + Value s2 = popQuiet(1); pushQuiet(1, s0); pushQuiet(1, s2); @@ -1818,8 +1829,8 @@ class Frame { if (get(sp - 1) == Long) { pushLongQuiet(c->peek(2, 0)); } else { - Compiler::Operand* s0 = popQuiet(1); - Compiler::Operand* s1 = popQuiet(1); + Value s0 = popQuiet(1); + Value s1 = popQuiet(1); pushQuiet(1, s1); pushQuiet(1, s0); @@ -1832,16 +1843,16 @@ class Frame { void dup2X1() { if (get(sp - 1) == Long) { - Compiler::Operand* s0 = popLongQuiet(); - Compiler::Operand* s1 = popQuiet(1); + Value s0 = popLongQuiet(); + Value s1 = popQuiet(1); pushLongQuiet(s0); pushQuiet(1, s1); pushLongQuiet(s0); } else { - Compiler::Operand* s0 = popQuiet(1); - Compiler::Operand* s1 = popQuiet(1); - Compiler::Operand* s2 = popQuiet(1); + Value s0 = popQuiet(1); + Value s1 = popQuiet(1); + Value s2 = popQuiet(1); pushQuiet(1, s1); pushQuiet(1, s0); @@ -1855,17 +1866,17 @@ class Frame { void dup2X2() { if (get(sp - 1) == Long) { - Compiler::Operand* s0 = popLongQuiet(); + Value s0 = popLongQuiet(); if (get(sp - 3) == Long) { - Compiler::Operand* s1 = popLongQuiet(); + Value s1 = popLongQuiet(); pushLongQuiet(s0); pushLongQuiet(s1); pushLongQuiet(s0); } else { - Compiler::Operand* s1 = popQuiet(1); - Compiler::Operand* s2 = popQuiet(1); + Value s1 = popQuiet(1); + Value s2 = popQuiet(1); pushLongQuiet(s0); pushQuiet(1, s2); @@ -1873,10 +1884,10 @@ class Frame { pushLongQuiet(s0); } } else { - Compiler::Operand* s0 = popQuiet(1); - Compiler::Operand* s1 = popQuiet(1); - Compiler::Operand* s2 = popQuiet(1); - Compiler::Operand* s3 = popQuiet(1); + Value s0 = popQuiet(1); + Value s1 = popQuiet(1); + Value s2 = popQuiet(1); + Value s3 = popQuiet(1); pushQuiet(1, s1); pushQuiet(1, s0); @@ -1890,8 +1901,8 @@ class Frame { } void swap() { - Compiler::Operand* s0 = popQuiet(1); - Compiler::Operand* s1 = popQuiet(1); + Value s0 = popQuiet(1); + Value s1 = popQuiet(1); pushQuiet(1, s0); pushQuiet(1, s1); @@ -1914,7 +1925,7 @@ class Frame { return e; } - unsigned startSubroutine(unsigned ip, Promise* returnAddress) { + unsigned startSubroutine(unsigned ip, avian::codegen::Promise* returnAddress) { pushAddress(absoluteAddressOperand(returnAddress)); Subroutine* subroutine = 0; @@ -1982,7 +1993,7 @@ class Frame { Context* context; MyThread* t; - Compiler* c; + avian::codegen::Compiler* c; Subroutine* subroutine; uint8_t* stackMap; unsigned ip; @@ -2187,7 +2198,7 @@ makeCurrentContinuation(MyThread* t, void** targetIp, void** targetStack) unsigned argumentFootprint = t->arch->argumentFootprint(methodParameterFootprint(t, target)); unsigned alignment = t->arch->stackAlignmentInWords(); - if (TailCalls and argumentFootprint > alignment) { + if (avian::codegen::TailCalls and argumentFootprint > alignment) { top += argumentFootprint - alignment; } @@ -2456,13 +2467,27 @@ getJClassFromReference(MyThread* t, object pair) referenceName(t, pairSecond(t, pair))))); } +bool +isNaN(double v) +{ + return fpclassify(v) == FP_NAN; +} + +bool +isNaN(float v) +{ + return fpclassify(v) == FP_NAN; +} + int64_t compareDoublesG(uint64_t bi, uint64_t ai) { double a = bitsToDouble(ai); double b = bitsToDouble(bi); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + return 1; + } else if (a < b) { return -1; } else if (a > b) { return 1; @@ -2479,7 +2504,9 @@ compareDoublesL(uint64_t bi, uint64_t ai) double a = bitsToDouble(ai); double b = bitsToDouble(bi); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + return -1; + } else if (a < b) { return -1; } else if (a > b) { return 1; @@ -2496,7 +2523,9 @@ compareFloatsG(uint32_t bi, uint32_t ai) float a = bitsToFloat(ai); float b = bitsToFloat(bi); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + return 1; + } if (a < b) { return -1; } else if (a > b) { return 1; @@ -2513,7 +2542,9 @@ compareFloatsL(uint32_t bi, uint32_t ai) float a = bitsToFloat(ai); float b = bitsToFloat(bi); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + return -1; + } if (a < b) { return -1; } else if (a > b) { return 1; @@ -3043,22 +3074,22 @@ getFieldValue(Thread* t, object target, object field) switch (fieldCode(t, field)) { case ByteField: case BooleanField: - return cast(target, fieldOffset(t, field)); + return fieldAtOffset(target, fieldOffset(t, field)); case CharField: case ShortField: - return cast(target, fieldOffset(t, field)); + return fieldAtOffset(target, fieldOffset(t, field)); case FloatField: case IntField: - return cast(target, fieldOffset(t, field)); + return fieldAtOffset(target, fieldOffset(t, field)); case DoubleField: case LongField: - return cast(target, fieldOffset(t, field)); + return fieldAtOffset(target, fieldOffset(t, field)); case ObjectField: - return cast(target, fieldOffset(t, field)); + return fieldAtOffset(target, fieldOffset(t, field)); default: abort(t); @@ -3101,7 +3132,7 @@ setStaticLongFieldValueFromReference(MyThread* t, object pair, uint64_t value) ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, fieldClass(t, field)), fieldOffset(t, field)) = value; } @@ -3116,7 +3147,7 @@ setLongFieldValueFromReference(MyThread* t, object pair, object instance, ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(instance, fieldOffset(t, field)) = value; + fieldAtOffset(instance, fieldOffset(t, field)) = value; } void @@ -3156,17 +3187,17 @@ setFieldValue(MyThread* t, object target, object field, uint32_t value) switch (fieldCode(t, field)) { case ByteField: case BooleanField: - cast(target, fieldOffset(t, field)) = value; + fieldAtOffset(target, fieldOffset(t, field)) = value; break; case CharField: case ShortField: - cast(target, fieldOffset(t, field)) = value; + fieldAtOffset(target, fieldOffset(t, field)) = value; break; case FloatField: case IntField: - cast(target, fieldOffset(t, field)) = value; + fieldAtOffset(target, fieldOffset(t, field)) = value; break; default: @@ -3372,11 +3403,11 @@ useLongJump(MyThread* t, uintptr_t target) Compiler::Operand* compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall, - bool useThunk, unsigned rSize, Promise* addressPromise) + bool useThunk, unsigned rSize, avian::codegen::Promise* addressPromise) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; - unsigned flags = (TailCalls and tailCall ? Compiler::TailJump : 0); + unsigned flags = (avian::codegen::TailCalls and tailCall ? Compiler::TailJump : 0); unsigned traceFlags; if (addressPromise == 0 and useLongJump(t, methodAddress(t, target))) { @@ -3387,18 +3418,18 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall, } if (useThunk - or (TailCalls and tailCall and (methodFlags(t, target) & ACC_NATIVE))) + or (avian::codegen::TailCalls and tailCall and (methodFlags(t, target) & ACC_NATIVE))) { if (frame->context->bootContext == 0) { flags |= Compiler::Aligned; } - if (TailCalls and tailCall) { + if (avian::codegen::TailCalls and tailCall) { traceFlags |= TraceElement::TailCall; TraceElement* trace = frame->trace(target, traceFlags); - Promise* returnAddressPromise = new + avian::codegen::Promise* returnAddressPromise = new (frame->context->zone.allocate(sizeof(TraceElementPromise))) TraceElementPromise(t->m->system, trace); @@ -3464,10 +3495,10 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall) if (bc) { if ((methodClass(t, target) == methodClass(t, frame->context->method) or (not classNeedsInit(t, methodClass(t, target)))) - and (not (TailCalls and tailCall + and (not (avian::codegen::TailCalls and tailCall and (methodFlags(t, target) & ACC_NATIVE)))) { - Promise* p = new(bc->zone) ListenPromise(t->m->system, bc->zone); + avian::codegen::Promise* p = new(bc->zone) avian::codegen::ListenPromise(t->m->system, bc->zone); PROTECT(t, target); object pointer = makePointer(t, p); @@ -3550,7 +3581,7 @@ void compileDirectReferenceInvoke(MyThread* t, Frame* frame, Thunk thunk, object reference, bool isStatic, bool tailCall) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; PROTECT(t, reference); @@ -3596,7 +3627,7 @@ void compileDirectAbstractInvoke(MyThread* t, Frame* frame, Thunk thunk, object target, bool tailCall) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; compileAbstractInvoke (t, frame, c->call @@ -3612,7 +3643,7 @@ compileDirectAbstractInvoke(MyThread* t, Frame* frame, Thunk thunk, void handleMonitorEvent(MyThread* t, Frame* frame, intptr_t function) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; object method = frame->context->method; if (methodFlags(t, method) & ACC_SYNCHRONIZED) { @@ -3719,52 +3750,53 @@ returnsNext(MyThread* t, object code, unsigned ip) bool isTailCall(MyThread* t, object code, unsigned ip, object caller, - int calleeReturnCode) + int calleeReturnCode, object calleeClassName, + object calleeMethodName, object calleeMethodSpec) { - return TailCalls + return avian::codegen::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) == calleeReturnCode) - and returnsNext(t, code, ip); + and returnsNext(t, code, ip) + and t->m->classpath->canTailCall + (t, caller, calleeClassName, calleeMethodName, calleeMethodSpec); } bool isTailCall(MyThread* t, object code, unsigned ip, object caller, object callee) { - return isTailCall(t, code, ip, caller, methodReturnCode(t, callee)); + return isTailCall + (t, code, ip, caller, methodReturnCode(t, callee), + className(t, methodClass(t, callee)), methodName(t, callee), + methodSpec(t, callee)); } bool isReferenceTailCall(MyThread* t, object code, unsigned ip, object caller, object calleeReference) { + object c = referenceClass(t, calleeReference); + if (objectClass(t, c) == type(t, Machine::ClassType)) { + c = className(t, c); + } + return isTailCall - (t, code, ip, caller, methodReferenceReturnCode(t, calleeReference)); -} - -void -compile(MyThread* t, Frame* initialFrame, unsigned ip, - int exceptionHandlerStart = -1); - -void -saveStateAndCompile(MyThread* t, Frame* initialFrame, unsigned ip) -{ - Compiler::State* state = initialFrame->c->saveState(); - compile(t, initialFrame, ip); - initialFrame->c->restoreState(state); + (t, code, ip, caller, methodReferenceReturnCode(t, calleeReference), + c, referenceName(t, calleeReference), referenceSpec(t, calleeReference)); } bool integerBranch(MyThread* t, Frame* frame, object code, unsigned& ip, - unsigned size, Compiler::Operand* a, Compiler::Operand* b) + unsigned size, Compiler::Operand* a, Compiler::Operand* b, + unsigned* newIpp) { if (ip + 3 > codeLength(t, code)) { return false; } - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; unsigned instruction = codeBody(t, code, ip++); uint32_t offset = codeReadInt16(t, code, ip); uint32_t newIp = (ip - 3) + offset; @@ -3802,20 +3834,20 @@ integerBranch(MyThread* t, Frame* frame, object code, unsigned& ip, return false; } - saveStateAndCompile(t, frame, newIp); + *newIpp = newIp; return true; } bool floatBranch(MyThread* t, Frame* frame, object code, unsigned& ip, unsigned size, bool lessIfUnordered, Compiler::Operand* a, - Compiler::Operand* b) + Compiler::Operand* b, unsigned* newIpp) { if (ip + 3 > codeLength(t, code)) { return false; } - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; unsigned instruction = codeBody(t, code, ip++); uint32_t offset = codeReadInt16(t, code, ip); uint32_t newIp = (ip - 3) + offset; @@ -3869,7 +3901,7 @@ floatBranch(MyThread* t, Frame* frame, object code, unsigned& ip, return false; } - saveStateAndCompile(t, frame, newIp); + *newIpp = newIp; return true; } @@ -3890,7 +3922,7 @@ intrinsic(MyThread* t, Frame* frame, object target) object className = vm::className(t, methodClass(t, target)); if (UNLIKELY(MATCH(className, "java/lang/Math"))) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; if (MATCH(methodName(t, target), "sqrt") and MATCH(methodSpec(t, target), "(D)D")) { @@ -3909,7 +3941,7 @@ intrinsic(MyThread* t, Frame* frame, object target) } } } else if (UNLIKELY(MATCH(className, "sun/misc/Unsafe"))) { - Compiler* c = frame->c; + avian::codegen::Compiler* c = frame->c; if (MATCH(methodName(t, target), "getByte") and MATCH(methodSpec(t, target), "(J)B")) { @@ -4043,17 +4075,126 @@ targetFieldOffset(Context* context, object field) } } -void -compile(MyThread* t, Frame* initialFrame, unsigned ip, - int exceptionHandlerStart) -{ - THREAD_RUNTIME_ARRAY(t, uint8_t, stackMap, - codeMaxStack(t, methodCode(t, initialFrame->context->method))); - Frame myFrame(initialFrame, RUNTIME_ARRAY_BODY(stackMap)); - Frame* frame = &myFrame; - Compiler* c = frame->c; - Context* context = frame->context; +class Stack { + public: + class MyResource: public Thread::Resource { + public: + MyResource(Stack* s): Resource(s->thread), s(s) { } + virtual void release() { + s->zone.dispose(); + } + + Stack* s; + }; + + Stack(MyThread* t): + thread(t), + zone(t->m->system, t->m->heap, 0), + resource(this) + { } + + ~Stack() { + zone.dispose(); + } + + void pushValue(uintptr_t v) { + *static_cast(push(BytesPerWord)) = v; + } + + uintptr_t peekValue(unsigned offset) { + return *static_cast(peek((offset + 1) * BytesPerWord)); + } + + uintptr_t popValue() { + uintptr_t v = peekValue(0); + pop(BytesPerWord); + return v; + } + + void* push(unsigned size) { + return zone.allocate(size); + } + + void* peek(unsigned size) { + return zone.peek(size); + } + + void pop(unsigned size) { + zone.pop(size); + } + + MyThread* thread; + Zone zone; + MyResource resource; +}; + +class SwitchState { + public: + SwitchState(Compiler::State* state, + unsigned count, + unsigned defaultIp, + Compiler::Operand* key, + avian::codegen::Promise* start, + int bottom, + int top): + state(state), + count(count), + defaultIp(defaultIp), + key(key), + start(start), + bottom(bottom), + top(top), + index(0) + { } + + Frame* frame() { + return reinterpret_cast + (reinterpret_cast(this) - pad(count * 4) - pad(sizeof(Frame))); + } + + uint32_t* ipTable() { + return reinterpret_cast + (reinterpret_cast(this) - pad(count * 4)); + } + + Compiler::State* state; + unsigned count; + unsigned defaultIp; + Compiler::Operand* key; + avian::codegen::Promise* start; + int bottom; + int top; + unsigned index; +}; + +void +compile(MyThread* t, Frame* initialFrame, unsigned initialIp, + int exceptionHandlerStart = -1) +{ + enum { + Return, + Unbranch, + Unsubroutine, + Untable0, + Untable1, + Unswitch + }; + + Frame* frame = initialFrame; + avian::codegen::Compiler* c = frame->c; + Context* context = frame->context; + unsigned stackSize = codeMaxStack(t, methodCode(t, context->method)); + Stack stack(t); + unsigned ip = initialIp; + unsigned newIp; + stack.pushValue(Return); + + start: + uint8_t* stackMap = static_cast(stack.push(stackSize)); + frame = new (stack.push(sizeof(Frame))) Frame(frame, stackMap); + + loop: object code = methodCode(t, context->method); PROTECT(t, code); @@ -4061,7 +4202,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, if (context->visitTable[ip] ++) { // we've already visited this part of the code frame->visitLogicalIp(ip); - return; + goto next; } frame->startLogicalIp(ip); @@ -4318,7 +4459,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case areturn: { handleExit(t, frame); c->return_(TargetBytesPerWord, frame->popObject()); - } return; + } goto next; case arraylength: { frame->pushInt @@ -4363,7 +4504,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, if (ip == codeLength(t, code)) { c->trap(); } - } return; + } goto next; case bipush: frame->pushInt @@ -4427,7 +4568,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popLong(); Compiler::Operand* b = frame->popLong(); - if (not floatBranch(t, frame, code, ip, 8, false, a, b)) { + if (floatBranch(t, frame, code, ip, 8, false, a, b, &newIp)) { + goto branch; + } else { frame->pushInt (c->call (c->constant @@ -4442,7 +4585,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popLong(); Compiler::Operand* b = frame->popLong(); - if (not floatBranch(t, frame, code, ip, 8, true, a, b)) { + if (floatBranch(t, frame, code, ip, 8, true, a, b, &newIp)) { + goto branch; + } else { frame->pushInt (c->call (c->constant @@ -4540,7 +4685,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popInt(); Compiler::Operand* b = frame->popInt(); - if (not floatBranch(t, frame, code, ip, 4, false, a, b)) { + if (floatBranch(t, frame, code, ip, 4, false, a, b, &newIp)) { + goto branch; + } else { frame->pushInt (c->call (c->constant @@ -4553,7 +4700,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popInt(); Compiler::Operand* b = frame->popInt(); - if (not floatBranch(t, frame, code, ip, 4, true, a, b)) { + if (floatBranch(t, frame, code, ip, 4, true, a, b, &newIp)) { + goto branch; + } else { frame->pushInt (c->call (c->constant @@ -4887,7 +5036,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case if_acmpeq: case if_acmpne: { uint32_t offset = codeReadInt16(t, code, ip); - uint32_t newIp = (ip - 3) + offset; + newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popObject(); @@ -4899,9 +5048,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { c->jumpIfNotEqual(TargetBytesPerWord, a, b, target); } - - saveStateAndCompile(t, frame, newIp); - } break; + } goto branch; case if_icmpeq: case if_icmpne: @@ -4910,7 +5057,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case if_icmplt: case if_icmple: { uint32_t offset = codeReadInt16(t, code, ip); - uint32_t newIp = (ip - 3) + offset; + newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = frame->popInt(); @@ -4939,9 +5086,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, default: abort(t); } - - saveStateAndCompile(t, frame, newIp); - } break; + } goto branch; case ifeq: case ifne: @@ -4950,7 +5095,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case iflt: case ifle: { uint32_t offset = codeReadInt16(t, code, ip); - uint32_t newIp = (ip - 3) + offset; + newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* target = frame->machineIp(newIp); @@ -4980,14 +5125,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, default: abort(t); } - - saveStateAndCompile(t, frame, newIp); - } break; + } goto branch; case ifnull: case ifnonnull: { uint32_t offset = codeReadInt16(t, code, ip); - uint32_t newIp = (ip - 3) + offset; + newIp = (ip - 3) + offset; assert(t, newIp < codeLength(t, code)); Compiler::Operand* a = c->constant(0, Compiler::ObjectType); @@ -4999,9 +5142,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } else { c->jumpIfNotEqual(TargetBytesPerWord, a, b, target); } - - saveStateAndCompile(t, frame, newIp); - } break; + } goto branch; case iinc: { uint8_t index = codeBody(t, code, ip++); @@ -5064,21 +5205,18 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object argument; Thunk thunk; - TraceElement* trace; if (LIKELY(class_)) { argument = class_; thunk = instanceOf64Thunk; - trace = 0; } else { argument = makePair(t, context->method, reference); thunk = instanceOfFromReferenceThunk; - trace = frame->trace(0, 0); } frame->pushInt (c->call (c->constant(getThunk(t, thunk), Compiler::AddressType), - 0, trace, 4, Compiler::IntegerType, + 0, frame->trace(0, 0), 4, Compiler::IntegerType, 3, c->register_(t->arch->thread()), frame->append(argument), instance)); } break; @@ -5301,7 +5439,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case freturn: { handleExit(t, frame); c->return_(4, frame->popInt()); - } return; + } goto next; case ishl: { Compiler::Operand* a = frame->popInt(); @@ -5361,7 +5499,6 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case jsr: case jsr_w: { uint32_t thisIp; - uint32_t newIp; if (instruction == jsr) { uint32_t offset = codeReadInt16(t, code, ip); @@ -5379,10 +5516,11 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, c->jmp(frame->machineIp(newIp)); - saveStateAndCompile(t, frame, newIp); - - frame->endSubroutine(start); - } break; + stack.pushValue(start); + stack.pushValue(ip); + stack.pushValue(Unsubroutine); + ip = newIp; + } goto start; case l2d: { frame->pushLong(c->i2f(8, 8, frame->popLong())); @@ -5412,7 +5550,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* a = frame->popLong(); Compiler::Operand* b = frame->popLong(); - if (not integerBranch(t, frame, code, ip, 8, a, b)) { + if (integerBranch(t, frame, code, ip, 8, a, b, &newIp)) { + goto branch; + } else { frame->pushInt (c->call (c->constant @@ -5569,17 +5709,18 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* default_ = frame->addressOperand (frame->addressPromise(c->machineIp(defaultIp))); - Promise* start = 0; - THREAD_RUNTIME_ARRAY(t, uint32_t, ipTable, pairCount); + avian::codegen::Promise* start = 0; + uint32_t* ipTable = static_cast + (stack.push(sizeof(uint32_t) * pairCount)); for (int32_t i = 0; i < pairCount; ++i) { unsigned index = ip + (i * 8); int32_t key = codeReadInt32(t, code, index); uint32_t newIp = base + codeReadInt32(t, code, index); assert(t, newIp < codeLength(t, code)); - RUNTIME_ARRAY_BODY(ipTable)[i] = newIp; + ipTable[i] = newIp; - Promise* p = c->poolAppend(key); + avian::codegen::Promise* p = c->poolAppend(key); if (i == 0) { start = p; } @@ -5601,19 +5742,15 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, TARGET_THREAD_CODEIMAGE), address) : address); - Compiler::State* state = c->saveState(); + new (stack.push(sizeof(SwitchState))) SwitchState + (c->saveState(), pairCount, defaultIp, 0, 0, 0, 0); - for (int32_t i = 0; i < pairCount; ++i) { - compile(t, frame, RUNTIME_ARRAY_BODY(ipTable)[i]); - - c->restoreState(state); - } + goto switchloop; } else { // a switch statement with no cases, apparently c->jmp(frame->machineIp(defaultIp)); + ip = defaultIp; } - - ip = defaultIp; } break; case lor: { @@ -5638,7 +5775,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case dreturn: { handleExit(t, frame); c->return_(8, frame->popLong()); - } return; + } goto next; case lshl: { Compiler::Operand* a = frame->popInt(); @@ -6065,7 +6202,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, case ret: { unsigned index = codeBody(t, code, ip); frame->returnFromSubroutine(index); - } return; + } goto next; case return_: if (needsReturnBarrier(t, context->method)) { @@ -6074,7 +6211,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, handleExit(t, frame); c->return_(0, 0); - return; + goto next; case sipush: frame->pushInt @@ -6098,16 +6235,18 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, int32_t bottom = codeReadInt32(t, code, ip); int32_t top = codeReadInt32(t, code, ip); - Promise* start = 0; - THREAD_RUNTIME_ARRAY(t, uint32_t, ipTable, top - bottom + 1); + avian::codegen::Promise* start = 0; + unsigned count = top - bottom + 1; + uint32_t* ipTable = static_cast + (stack.push(sizeof(uint32_t) * count)); for (int32_t i = 0; i < top - bottom + 1; ++i) { unsigned index = ip + (i * 4); uint32_t newIp = base + codeReadInt32(t, code, index); assert(t, newIp < codeLength(t, code)); - RUNTIME_ARRAY_BODY(ipTable)[i] = newIp; + ipTable[i] = newIp; - Promise* p = c->poolAppendPromise + avian::codegen::Promise* p = c->poolAppendPromise (frame->addressPromise(c->machineIp(newIp))); if (i == 0) { start = p; @@ -6122,43 +6261,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, c->save(1, key); - saveStateAndCompile(t, frame, defaultIp); - - c->jumpIfGreater(4, c->constant(top, Compiler::IntegerType), key, - frame->machineIp(defaultIp)); - - c->save(1, key); - - saveStateAndCompile(t, frame, defaultIp); - - Compiler::Operand* normalizedKey - = (bottom - ? c->sub(4, c->constant(bottom, Compiler::IntegerType), key) : key); - - Compiler::Operand* entry = c->memory - (frame->absoluteAddressOperand(start), Compiler::AddressType, 0, - normalizedKey, TargetBytesPerWord); - - c->jmp - (c->load - (TargetBytesPerWord, TargetBytesPerWord, context->bootContext - ? c->add - (TargetBytesPerWord, c->memory - (c->register_(t->arch->thread()), Compiler::AddressType, - TARGET_THREAD_CODEIMAGE), entry) - : entry, - TargetBytesPerWord)); - - Compiler::State* state = c->saveState(); - - for (int32_t i = 0; i < top - bottom + 1; ++i) { - compile(t, frame, RUNTIME_ARRAY_BODY(ipTable)[i]); - - c->restoreState(state); - } + new (stack.push(sizeof(SwitchState))) SwitchState + (c->saveState(), count, defaultIp, key, start, bottom, top); + stack.pushValue(Untable0); ip = defaultIp; - } break; + } goto start; case wide: { switch (codeBody(t, code, ip++)) { @@ -6202,7 +6310,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, unsigned index = codeReadInt16(t, code, ip); c->jmp(loadLocal(context, 1, index)); frame->returnFromSubroutine(index); - } return; + } goto next; default: abort(t); } @@ -6211,6 +6319,113 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, default: abort(t); } } + + next: + frame->dispose(); + frame = 0; + stack.pop(sizeof(Frame)); + stack.pop(stackSize); + switch (stack.popValue()) { + case Return: + return; + + case Unbranch: + ip = stack.popValue(); + c->restoreState(reinterpret_cast(stack.popValue())); + frame = static_cast(stack.peek(sizeof(Frame))); + goto loop; + + case Untable0: { + SwitchState* s = static_cast + (stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState(s->state); + + c->jumpIfGreater(4, c->constant(s->top, Compiler::IntegerType), s->key, + frame->machineIp(s->defaultIp)); + + c->save(1, s->key); + ip = s->defaultIp; + stack.pushValue(Untable1); + } goto start; + + case Untable1: { + SwitchState* s = static_cast + (stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState(s->state); + + Compiler::Operand* normalizedKey + = (s->bottom + ? c->sub(4, c->constant(s->bottom, Compiler::IntegerType), s->key) + : s->key); + + Compiler::Operand* entry = c->memory + (frame->absoluteAddressOperand(s->start), Compiler::AddressType, 0, + normalizedKey, TargetBytesPerWord); + + c->jmp + (c->load + (TargetBytesPerWord, TargetBytesPerWord, context->bootContext + ? c->add + (TargetBytesPerWord, c->memory + (c->register_(t->arch->thread()), Compiler::AddressType, + TARGET_THREAD_CODEIMAGE), entry) + : entry, + TargetBytesPerWord)); + + s->state = c->saveState(); + } goto switchloop; + + case Unswitch: { + SwitchState* s = static_cast + (stack.peek(sizeof(SwitchState))); + + frame = s->frame(); + + c->restoreState + (static_cast(stack.peek(sizeof(SwitchState)))->state); + } goto switchloop; + + case Unsubroutine: { + ip = stack.popValue(); + unsigned start = stack.popValue(); + frame = reinterpret_cast(stack.peek(sizeof(Frame))); + frame->endSubroutine(start); + } goto loop; + + default: + abort(t); + } + + switchloop: { + SwitchState* s = static_cast + (stack.peek(sizeof(SwitchState))); + + if (s->index < s->count) { + ip = s->ipTable()[s->index++]; + stack.pushValue(Unswitch); + goto start; + } else { + ip = s->defaultIp; + unsigned count = s->count * 4; + stack.pop(sizeof(SwitchState)); + stack.pop(count); + frame = reinterpret_cast(stack.peek(sizeof(Frame))); + goto loop; + } + } + + branch: + stack.pushValue(reinterpret_cast(c->saveState())); + stack.pushValue(ip); + stack.pushValue(Unbranch); + ip = newIp; + goto start; } FILE* compileLog = 0; @@ -6244,11 +6459,11 @@ resolveIpBackwards(Context* context, int start, int end) if (start >= static_cast (codeLength(t, methodCode(t, context->method)))) { - start = codeLength(t, methodCode(t, context->method)) - 1; - } - - while (start >= end and context->visitTable[start] == 0) { - -- start; + start = codeLength(t, methodCode(t, context->method)); + } else { + while (start >= end and context->visitTable[start] == 0) { + -- start; + } } if (start < end) { @@ -6308,9 +6523,10 @@ truncateLineNumberTable(Thread* t, object table, unsigned length) } object -translateExceptionHandlerTable(MyThread* t, Context* context, intptr_t start) +translateExceptionHandlerTable(MyThread* t, Context* context, intptr_t start, + intptr_t end) { - Compiler* c = context->compiler; + avian::codegen::Compiler* c = context->compiler; object oldTable = codeExceptionHandlerTable (t, methodCode(t, context->method)); @@ -6344,14 +6560,16 @@ translateExceptionHandlerTable(MyThread* t, Context* context, intptr_t start) exceptionHandlerStart(oldHandler)); assert(t, handlerEnd >= 0); - assert(t, handlerEnd < static_cast + assert(t, handlerEnd <= static_cast (codeLength(t, methodCode(t, context->method)))); intArrayBody(t, newIndex, ni * 3) = c->machineIp(handlerStart)->value() - start; intArrayBody(t, newIndex, (ni * 3) + 1) - = c->machineIp(handlerEnd)->value() - start; + = (handlerEnd == static_cast + (codeLength(t, methodCode(t, context->method))) + ? end : c->machineIp(handlerEnd)->value()) - start; intArrayBody(t, newIndex, (ni * 3) + 2) = c->machineIp(exceptionHandlerIp(oldHandler))->value() - start; @@ -6419,11 +6637,11 @@ translateLineNumberTable(MyThread* t, Context* context, intptr_t start) } void -printSet(uintptr_t m, unsigned limit) +printSet(uintptr_t* m, unsigned limit) { if (limit) { - for (unsigned i = 0; i < 16; ++i) { - if ((m >> i) & 1) { + for (unsigned i = 0; i < 32; ++i) { + if ((*m >> i) & 1) { fprintf(stderr, "1"); } else { fprintf(stderr, "_"); @@ -6467,7 +6685,7 @@ calculateTryCatchRoots(Context* context, SubroutinePath* subroutinePath, if (traceRoots) { if (DebugFrameMaps) { fprintf(stderr, " use roots at ip %3d: ", te->ip); - printSet(*traceRoots, mapSize); + printSet(traceRoots, mapSize); fprintf(stderr, "\n"); } @@ -6484,7 +6702,7 @@ calculateTryCatchRoots(Context* context, SubroutinePath* subroutinePath, if (DebugFrameMaps) { fprintf(stderr, "result roots : "); - printSet(*roots, mapSize); + printSet(roots, mapSize); fprintf(stderr, "\n"); } } @@ -6538,7 +6756,7 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, if (DebugFrameMaps) { fprintf(stderr, " roots at ip %3d: ", ip); - printSet(*RUNTIME_ARRAY_BODY(roots), mapSize); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); fprintf(stderr, "\n"); } @@ -6567,7 +6785,7 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, if (DebugFrameMaps) { fprintf(stderr, " table roots at ip %3d: ", ip); - printSet(*tableRoots, mapSize); + printSet(tableRoots, mapSize); fprintf(stderr, "\n"); } } else { @@ -6621,7 +6839,7 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, TraceElement* te; context->eventLog.get(eventIndex, &te, BytesPerWord); if (DebugFrameMaps) { fprintf(stderr, " trace roots at ip %3d: ", ip); - printSet(*RUNTIME_ARRAY_BODY(roots), mapSize); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); if (subroutinePath) { fprintf(stderr, " "); print(subroutinePath); @@ -6720,7 +6938,7 @@ calculateFrameMaps(MyThread* t, Context* context, uintptr_t* originalRoots, if (resultRoots and ip != -1) { if (DebugFrameMaps) { fprintf(stderr, "result roots at ip %3d: ", ip); - printSet(*RUNTIME_ARRAY_BODY(roots), mapSize); + printSet(RUNTIME_ARRAY_BODY(roots), mapSize); if (subroutinePath) { fprintf(stderr, " "); print(subroutinePath); @@ -6752,11 +6970,11 @@ unsigned simpleFrameMapTableSize(MyThread* t, object method, object map) { int size = frameMapSizeInBits(t, method); - return ceiling(intArrayLength(t, map) * size, 32 + size); + return ceilingDivide(intArrayLength(t, map) * size, 32 + size); } uint8_t* -finish(MyThread* t, FixedAllocator* allocator, Assembler* a, const char* name, +finish(MyThread* t, FixedAllocator* allocator, avian::codegen::Assembler* a, const char* name, unsigned length) { uint8_t* start = static_cast @@ -6789,7 +7007,7 @@ copyFrameMap(int32_t* dst, uintptr_t* src, unsigned mapSizeInBits, { if (DebugFrameMaps) { fprintf(stderr, " orig roots at ip %3d: ", p->ip); - printSet(src[0], ceiling(mapSizeInBits, BitsPerWord)); + printSet(src, ceilingDivide(mapSizeInBits, BitsPerWord)); print(subroutinePath); fprintf(stderr, "\n"); @@ -6892,7 +7110,7 @@ makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start, unsigned indexOffset = sizeof(FrameMapTableHeader); unsigned mapsOffset = indexOffset + (elementCount * sizeof(FrameMapTableIndexElement)); - unsigned pathsOffset = mapsOffset + (ceiling(mapCount * mapSize, 32) * 4); + unsigned pathsOffset = mapsOffset + (ceilingDivide(mapCount * mapSize, 32) * 4); object table = makeByteArray(t, pathsOffset + pathFootprint); @@ -6967,7 +7185,7 @@ makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start, sizeof(SubroutineTrace*), compareSubroutineTracePointers); for (unsigned i = 0; i < p->subroutineTraceCount; ++i) { - assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + assert(t, mapsOffset + ceilingDivide(nextMapIndex + mapSize, 32) * 4 <= pathsOffset); copyFrameMap(reinterpret_cast(body + mapsOffset), @@ -6979,7 +7197,7 @@ makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start, } else { pathIndex = 0; - assert(t, mapsOffset + ceiling(nextMapIndex + mapSize, 32) * 4 + assert(t, mapsOffset + ceilingDivide(nextMapIndex + mapSize, 32) * 4 <= pathsOffset); copyFrameMap(reinterpret_cast(body + mapsOffset), p->map, @@ -7009,7 +7227,7 @@ makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start, { unsigned mapSize = frameMapSizeInBits(t, context->method); object table = makeIntArray - (t, elementCount + ceiling(elementCount * mapSize, 32)); + (t, elementCount + ceilingDivide(elementCount * mapSize, 32)); assert(t, intArrayLength(t, table) == elementCount + simpleFrameMapTableSize(t, context->method, table)); @@ -7020,7 +7238,7 @@ makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start, intArrayBody(t, table, i) = static_cast(p->address->value()) - reinterpret_cast(start); - assert(t, elementCount + ceiling((i + 1) * mapSize, 32) + assert(t, elementCount + ceilingDivide((i + 1) * mapSize, 32) <= intArrayLength(t, table)); if (mapSize) { @@ -7035,7 +7253,7 @@ makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start, void finish(MyThread* t, FixedAllocator* allocator, Context* context) { - Compiler* c = context->compiler; + avian::codegen::Compiler* c = context->compiler; if (false) { logCompile @@ -7092,6 +7310,9 @@ finish(MyThread* t, FixedAllocator* allocator, Context* context) FixedSizeOfArray + ((context->objectPoolCount + 1) * BytesPerWord), true); + context->executableSize = (allocator->base + allocator->offset) + - static_cast(context->executableStart); + initArray(t, pool, context->objectPoolCount + 1); mark(t, pool, 0); @@ -7112,16 +7333,17 @@ finish(MyThread* t, FixedAllocator* allocator, Context* context) BootContext* bc = context->bootContext; if (bc) { - for (DelayedPromise* p = bc->addresses; + for (avian::codegen::DelayedPromise* p = bc->addresses; p != bc->addressSentinal; p = p->next) { - p->basis = new(bc->zone) ResolvedPromise(p->basis->value()); + p->basis = new(bc->zone) avian::codegen::ResolvedPromise(p->basis->value()); } } { object newExceptionHandlerTable = translateExceptionHandlerTable - (t, context, reinterpret_cast(start)); + (t, context, reinterpret_cast(start), + reinterpret_cast(start) + codeSize); PROTECT(t, newExceptionHandlerTable); @@ -7213,14 +7435,15 @@ finish(MyThread* t, FixedAllocator* allocator, Context* context) { trap(); } - +#if !defined(AVIAN_AOT_ONLY) syncInstructionCache(start, codeSize); +#endif } void compile(MyThread* t, Context* context) { - Compiler* c = context->compiler; + avian::codegen::Compiler* c = context->compiler; // fprintf(stderr, "compiling %s.%s%s\n", // &byteArrayBody(t, className(t, methodClass(t, context->method)), 0), @@ -7354,7 +7577,7 @@ compile(MyThread* t, Context* context) } void -updateCall(MyThread* t, UnaryOperation op, void* returnAddress, void* target) +updateCall(MyThread* t, avian::codegen::lir::UnaryOperation op, void* returnAddress, void* target) { t->arch->updateCall(op, returnAddress, target); } @@ -7647,7 +7870,7 @@ invokeNative(MyThread* t) uintptr_t* stack = static_cast(t->stack); - if (TailCalls + if (avian::codegen::TailCalls and t->arch->argumentFootprint(parameterFootprint) > t->arch->stackAlignmentInWords()) { @@ -8322,11 +8545,11 @@ class ArgumentList { case 'J': case 'D': - addLong(cast(objectArrayBody(t, arguments, index++), 8)); + addLong(fieldAtOffset(objectArrayBody(t, arguments, index++), 8)); break; default: - addInt(cast(objectArrayBody(t, arguments, index++), + addInt(fieldAtOffset(objectArrayBody(t, arguments, index++), BytesPerWord)); break; } @@ -8976,7 +9199,9 @@ class MyProcessor: public Processor { virtual void dispose() { if (codeAllocator.base) { +#if !defined(AVIAN_AOT_ONLY) s->freeExecutable(codeAllocator.base, codeAllocator.capacity); +#endif } compilationHandlers->dispose(allocator); @@ -9082,7 +9307,7 @@ class MyProcessor: public Processor { } virtual void compileMethod(Thread* vmt, Zone* zone, object* constants, - object* calls, DelayedPromise** addresses, + object* calls, avian::codegen::DelayedPromise** addresses, object method, OffsetResolver* resolver) { MyThread* t = static_cast(vmt); @@ -9137,11 +9362,13 @@ class MyProcessor: public Processor { } virtual void boot(Thread* t, BootImage* image, uint8_t* code) { +#if !defined(AVIAN_AOT_ONLY) if (codeAllocator.base == 0) { codeAllocator.base = static_cast (s->tryAllocateExecutable(ExecutableAreaSizeInBytes)); codeAllocator.capacity = ExecutableAreaSizeInBytes; } +#endif if (image and code) { local::boot(static_cast(t), image, code); @@ -9158,11 +9385,15 @@ class MyProcessor: public Processor { root(t, MethodTreeSentinal)); } +#ifdef AVIAN_AOT_ONLY + thunks = bootThunks; +#else local::compileThunks(static_cast(t), &codeAllocator); if (not (image and code)) { bootThunks = thunks; } +#endif segFaultHandler.m = t->m; expect(t, t->m->system->success @@ -9269,7 +9500,7 @@ logCompile(MyThread* t, const void* code, unsigned size, const char* class_, } if (compileLog) { - fprintf(compileLog, "%p %p %s.%s%s\n", + fprintf(compileLog, "%p,%p %s.%s%s\n", code, static_cast(code) + size, class_, name, spec); } @@ -9317,17 +9548,17 @@ compileMethod2(MyThread* t, void* ip) } if (updateCaller) { - UnaryOperation op; + avian::codegen::lir::UnaryOperation op; if (callNodeFlags(t, node) & TraceElement::LongCall) { if (callNodeFlags(t, node) & TraceElement::TailCall) { - op = AlignedLongJump; + op = avian::codegen::lir::AlignedLongJump; } else { - op = AlignedLongCall; + op = avian::codegen::lir::AlignedLongCall; } } else if (callNodeFlags(t, node) & TraceElement::TailCall) { - op = AlignedJump; + op = avian::codegen::lir::AlignedJump; } else { - op = AlignedCall; + op = avian::codegen::lir::AlignedCall; } updateCall(t, op, updateIp, reinterpret_cast(address)); @@ -9749,7 +9980,7 @@ boot(MyThread* t, BootImage* image, uint8_t* code) uintptr_t* heapMap = reinterpret_cast (padWord(reinterpret_cast(callTable + (image->callCount * 2)))); - unsigned heapMapSizeInWords = ceiling + unsigned heapMapSizeInWords = ceilingDivide (heapMapSize(image->heapSize), BytesPerWord); uintptr_t* heap = heapMap + heapMapSizeInWords; @@ -9758,7 +9989,7 @@ boot(MyThread* t, BootImage* image, uint8_t* code) t->heapImage = p->heapImage = heap; // fprintf(stderr, "heap from %p to %p\n", - // heap, heap + ceiling(image->heapSize, BytesPerWord)); + // heap, heap + ceilingDivide(image->heapSize, BytesPerWord)); t->codeImage = p->codeImage = code; p->codeImageSize = image->codeSize; @@ -9857,27 +10088,34 @@ thunkToThunk(const MyProcessor::Thunk& thunk, uint8_t* base) (thunk.start - base, thunk.frameSavedOffset, thunk.length); } +using avian::codegen::OperandInfo; +namespace lir = avian::codegen::lir; + void compileCall(MyThread* t, Context* c, ThunkIndex index, bool call = true) { - Assembler* a = c->assembler; + avian::codegen::Assembler* a = c->assembler; if (processor(t)->bootImage) { - Assembler::Memory table(t->arch->thread(), TARGET_THREAD_THUNKTABLE); - Assembler::Register scratch(t->arch->scratch()); - a->apply(Move, TargetBytesPerWord, MemoryOperand, &table, - TargetBytesPerWord, RegisterOperand, &scratch); - Assembler::Memory proc(scratch.low, index * TargetBytesPerWord); - a->apply(Move, TargetBytesPerWord, MemoryOperand, &proc, - TargetBytesPerWord, RegisterOperand, &scratch); + lir::Memory table(t->arch->thread(), TARGET_THREAD_THUNKTABLE); + lir::Register scratch(t->arch->scratch()); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &table), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &scratch)); + lir::Memory proc(scratch.low, index * TargetBytesPerWord); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &proc), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &scratch)); a->apply - (call ? Call : Jump, TargetBytesPerWord, RegisterOperand, &scratch); + (call ? lir::Call : lir::Jump, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &scratch)); } else { - Assembler::Constant proc - (new(&c->zone) ResolvedPromise(reinterpret_cast(t->thunkTable[index]))); + lir::Constant proc + (new(&c->zone) avian::codegen::ResolvedPromise(reinterpret_cast(t->thunkTable[index]))); a->apply - (call ? LongCall : LongJump, TargetBytesPerWord, ConstantOperand, &proc); + (call ? lir::LongCall : lir::LongJump, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &proc)); } } @@ -9887,21 +10125,22 @@ compileThunks(MyThread* t, FixedAllocator* allocator) MyProcessor* p = processor(t); { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); p->thunks.default_.frameSavedOffset = a->length(); - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, RegisterOperand, &thread); + lir::Register thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::RegisterOperand, &thread); compileCall(t, &context, compileMethodIndex); a->popFrame(t->arch->alignFrameSize(1)); - Assembler::Register result(t->arch->returnLow()); - a->apply(Jump, TargetBytesPerWord, RegisterOperand, &result); + lir::Register result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &result)); p->thunks.default_.length = a->endBlock(false)->resolve(0, 0); @@ -9910,43 +10149,47 @@ compileThunks(MyThread* t, FixedAllocator* allocator) } { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; - Assembler::Register class_(t->arch->virtualCallTarget()); - Assembler::Memory virtualCallTargetSrc + lir::Register class_(t->arch->virtualCallTarget()); + lir::Memory virtualCallTargetSrc (t->arch->stack(), (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) * TargetBytesPerWord); - a->apply(Move, TargetBytesPerWord, MemoryOperand, &virtualCallTargetSrc, - TargetBytesPerWord, RegisterOperand, &class_); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &virtualCallTargetSrc), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &class_)); - Assembler::Memory virtualCallTargetDst + lir::Memory virtualCallTargetDst (t->arch->thread(), TARGET_THREAD_VIRTUALCALLTARGET); - a->apply(Move, TargetBytesPerWord, RegisterOperand, &class_, - TargetBytesPerWord, MemoryOperand, &virtualCallTargetDst); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &class_), + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &virtualCallTargetDst)); - Assembler::Register index(t->arch->virtualCallIndex()); - Assembler::Memory virtualCallIndex + lir::Register index(t->arch->virtualCallIndex()); + lir::Memory virtualCallIndex (t->arch->thread(), TARGET_THREAD_VIRTUALCALLINDEX); - a->apply(Move, TargetBytesPerWord, RegisterOperand, &index, - TargetBytesPerWord, MemoryOperand, &virtualCallIndex); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &index), + OperandInfo(TargetBytesPerWord, lir::MemoryOperand, &virtualCallIndex)); a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); p->thunks.defaultVirtual.frameSavedOffset = a->length(); - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, RegisterOperand, &thread); + lir::Register thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::RegisterOperand, &thread); compileCall(t, &context, compileVirtualMethodIndex); a->popFrame(t->arch->alignFrameSize(1)); - Assembler::Register result(t->arch->returnLow()); - a->apply(Jump, TargetBytesPerWord, RegisterOperand, &result); + lir::Register result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &result)); p->thunks.defaultVirtual.length = a->endBlock(false)->resolve(0, 0); @@ -9955,14 +10198,14 @@ compileThunks(MyThread* t, FixedAllocator* allocator) } { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); p->thunks.native.frameSavedOffset = a->length(); - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, RegisterOperand, &thread); + lir::Register thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::RegisterOperand, &thread); compileCall(t, &context, invokeNativeIndex); @@ -9976,14 +10219,14 @@ compileThunks(MyThread* t, FixedAllocator* allocator) } { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); p->thunks.aioob.frameSavedOffset = a->length(); - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, RegisterOperand, &thread); + lir::Register thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::RegisterOperand, &thread); compileCall(t, &context, throwArrayIndexOutOfBoundsIndex); @@ -9994,14 +10237,14 @@ compileThunks(MyThread* t, FixedAllocator* allocator) } { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); p->thunks.stackOverflow.frameSavedOffset = a->length(); - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, RegisterOperand, &thread); + lir::Register thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::RegisterOperand, &thread); compileCall(t, &context, throwStackOverflowIndex); @@ -10012,7 +10255,7 @@ compileThunks(MyThread* t, FixedAllocator* allocator) } { { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); @@ -10031,7 +10274,7 @@ compileThunks(MyThread* t, FixedAllocator* allocator) #define THUNK(s) { \ Context context(t); \ - Assembler* a = context.assembler; \ + avian::codegen::Assembler* a = context.assembler; \ \ a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); \ \ @@ -10129,17 +10372,19 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) { Context context(t); - Assembler* a = context.assembler; + avian::codegen::Assembler* a = context.assembler; - ResolvedPromise indexPromise(index); - Assembler::Constant indexConstant(&indexPromise); - Assembler::Register indexRegister(t->arch->virtualCallIndex()); - a->apply(Move, TargetBytesPerWord, ConstantOperand, &indexConstant, - TargetBytesPerWord, RegisterOperand, &indexRegister); + avian::codegen::ResolvedPromise indexPromise(index); + lir::Constant indexConstant(&indexPromise); + lir::Register indexRegister(t->arch->virtualCallIndex()); + a->apply(lir::Move, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &indexConstant), + OperandInfo(TargetBytesPerWord, lir::RegisterOperand, &indexRegister)); - ResolvedPromise defaultVirtualThunkPromise(defaultVirtualThunk(t)); - Assembler::Constant thunk(&defaultVirtualThunkPromise); - a->apply(Jump, TargetBytesPerWord, ConstantOperand, &thunk); + avian::codegen::ResolvedPromise defaultVirtualThunkPromise(defaultVirtualThunk(t)); + lir::Constant thunk(&defaultVirtualThunkPromise); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::ConstantOperand, &thunk)); *size = a->endBlock(false)->resolve(0, 0); diff --git a/src/compiler.cpp b/src/compiler.cpp deleted file mode 100644 index 8e118008bb..0000000000 --- a/src/compiler.cpp +++ /dev/null @@ -1,6962 +0,0 @@ -/* Copyright (c) 2008-2012, 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 "compiler.h" -#include "assembler.h" -#include "target.h" - -using namespace vm; - -namespace { - -namespace local { - -const bool DebugAppend = false; -const bool DebugCompile = false; -const bool DebugResources = false; -const bool DebugFrame = false; -const bool DebugControl = false; -const bool DebugReads = false; -const bool DebugSites = false; -const bool DebugMoves = false; -const bool DebugBuddies = false; - -const int AnyFrameIndex = -2; -const int NoFrameIndex = -1; - -const unsigned StealRegisterReserveCount = 2; - -// this should be equal to the largest number of registers used by a -// compare instruction: -const unsigned ResolveRegisterReserveCount = (TargetBytesPerWord == 8 ? 2 : 4); - -const unsigned RegisterCopyCost = 1; -const unsigned AddressCopyCost = 2; -const unsigned ConstantCopyCost = 3; -const unsigned MemoryCopyCost = 4; -const unsigned CopyPenalty = 10; - -class Context; -class Value; -class Stack; -class Site; -class ConstantSite; -class AddressSite; -class RegisterSite; -class MemorySite; -class Event; -class PushEvent; -class Read; -class MultiRead; -class StubRead; -class Block; -class Snapshot; - -void NO_RETURN abort(Context*); - -void -apply(Context* c, UnaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High); - -void -apply(Context* c, BinaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High, - unsigned s2Size, Site* s2Low, Site* s2High); - -void -apply(Context* c, TernaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High, - unsigned s2Size, Site* s2Low, Site* s2High, - unsigned s3Size, Site* s3Low, Site* s3High); - -class Cell { - public: - Cell(Cell* next, void* value): next(next), value(value) { } - - Cell* next; - void* value; -}; - -class Local { - public: - Value* value; -}; - -class SiteMask { - public: - SiteMask(): typeMask(~0), registerMask(~0), frameIndex(AnyFrameIndex) { } - - SiteMask(uint8_t typeMask, uint32_t registerMask, int frameIndex): - typeMask(typeMask), registerMask(registerMask), frameIndex(frameIndex) - { } - - uint8_t typeMask; - uint32_t registerMask; - int frameIndex; -}; - -class Site { - public: - Site(): next(0) { } - - virtual Site* readTarget(Context*, Read*) { return this; } - - virtual unsigned toString(Context*, char*, unsigned) = 0; - - virtual unsigned copyCost(Context*, Site*) = 0; - - virtual bool match(Context*, const SiteMask&) = 0; - - virtual bool loneMatch(Context*, const SiteMask&) = 0; - - virtual bool matchNextWord(Context*, Site*, unsigned) = 0; - - virtual void acquire(Context*, Value*) { } - - virtual void release(Context*, Value*) { } - - virtual void freeze(Context*, Value*) { } - - virtual void thaw(Context*, Value*) { } - - virtual bool frozen(Context*) { return false; } - - virtual OperandType type(Context*) = 0; - - virtual void asAssemblerOperand(Context*, Site*, Assembler::Operand*) = 0; - - virtual Site* copy(Context*) = 0; - - virtual Site* copyLow(Context*) = 0; - - virtual Site* copyHigh(Context*) = 0; - - virtual Site* makeNextWord(Context*, unsigned) = 0; - - virtual SiteMask mask(Context*) = 0; - - virtual SiteMask nextWordMask(Context*, unsigned) = 0; - - virtual unsigned registerSize(Context*) { return TargetBytesPerWord; } - - virtual unsigned registerMask(Context*) { return 0; } - - virtual bool isVolatile(Context*) { return false; } - - Site* next; -}; - -class Stack { - public: - Stack(unsigned index, Value* value, Stack* next): - index(index), value(value), next(next) - { } - - unsigned index; - Value* value; - Stack* next; -}; - -class ForkElement { - public: - Value* value; - MultiRead* read; - bool local; -}; - -class ForkState: public Compiler::State { - public: - ForkState(Stack* stack, Local* locals, Cell* saved, Event* predecessor, - unsigned logicalIp): - stack(stack), - locals(locals), - saved(saved), - predecessor(predecessor), - logicalIp(logicalIp), - readCount(0) - { } - - Stack* stack; - Local* locals; - Cell* saved; - Event* predecessor; - unsigned logicalIp; - unsigned readCount; - ForkElement elements[0]; -}; - -class MySubroutine: public Compiler::Subroutine { - public: - MySubroutine(): forkState(0) { } - - ForkState* forkState; -}; - -class LogicalInstruction { - public: - LogicalInstruction(int index, Stack* stack, Local* locals): - firstEvent(0), lastEvent(0), immediatePredecessor(0), stack(stack), - locals(locals), machineOffset(0), subroutine(0), index(index) - { } - - Event* firstEvent; - Event* lastEvent; - LogicalInstruction* immediatePredecessor; - Stack* stack; - Local* locals; - Promise* machineOffset; - MySubroutine* subroutine; - int index; -}; - -class Resource { - public: - Resource(bool reserved = false): - value(0), site(0), previousAcquired(0), nextAcquired(0), freezeCount(0), - referenceCount(0), reserved(reserved) - { } - - virtual void freeze(Context*, Value*) = 0; - - virtual void thaw(Context*, Value*) = 0; - - virtual unsigned toString(Context*, char*, unsigned) = 0; - - Value* value; - Site* site; - Resource* previousAcquired; - Resource* nextAcquired; - uint8_t freezeCount; - uint8_t referenceCount; - bool reserved; -}; - -class RegisterResource: public Resource { - public: - RegisterResource(bool reserved): - Resource(reserved) - { } - - virtual void freeze(Context*, Value*); - - virtual void thaw(Context*, Value*); - - virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize) { - return vm::snprintf(buffer, bufferSize, "register %d", index(c)); - } - - virtual unsigned index(Context*); -}; - -class FrameResource: public Resource { - public: - virtual void freeze(Context*, Value*); - - virtual void thaw(Context*, Value*); - - virtual unsigned toString(Context* c, char* buffer, unsigned bufferSize) { - return vm::snprintf(buffer, bufferSize, "frame %d", index(c)); - } - - virtual unsigned index(Context*); -}; - -class ConstantPoolNode { - public: - ConstantPoolNode(Promise* promise): promise(promise), next(0) { } - - Promise* promise; - ConstantPoolNode* next; -}; - -class Read { - public: - Read(): - value(0), event(0), eventNext(0) - { } - - virtual bool intersect(SiteMask* mask, unsigned depth = 0) = 0; - - virtual Value* high(Context* c) { abort(c); } - - virtual Value* successor() = 0; - - virtual bool valid() = 0; - - virtual void append(Context* c, Read* r) = 0; - - virtual Read* next(Context* c) = 0; - - Value* value; - Event* event; - Read* eventNext; -}; - -int -intersectFrameIndexes(int a, int b) -{ - if (a == NoFrameIndex or b == NoFrameIndex) return NoFrameIndex; - if (a == AnyFrameIndex) return b; - if (b == AnyFrameIndex) return a; - if (a == b) return a; - return NoFrameIndex; -} - -SiteMask -intersect(const SiteMask& a, const SiteMask& b) -{ - return SiteMask(a.typeMask & b.typeMask, a.registerMask & b.registerMask, - intersectFrameIndexes(a.frameIndex, b.frameIndex)); -} - -class Value: public Compiler::Operand { - public: - Value(Site* site, Site* target, ValueType type): - reads(0), lastRead(0), sites(site), source(0), target(target), buddy(this), - nextWord(this), home(NoFrameIndex), type(type), wordIndex(0) - { } - - Read* reads; - Read* lastRead; - Site* sites; - Site* source; - Site* target; - Value* buddy; - Value* nextWord; - int16_t home; - ValueType type; - uint8_t wordIndex; -}; - -uint32_t -registerMask(Assembler::Architecture* arch) -{ - return arch->generalRegisterMask() | arch->floatRegisterMask(); -} - -unsigned -maskStart(uint32_t mask) -{ - for (int i = 0; i <= 31; ++i) { - if (mask & (1 << i)) return i; - } - return 32; -} - -unsigned -maskLimit(uint32_t mask) -{ - for (int i = 31; i >= 0; --i) { - if (mask & (1 << i)) return i + 1; - } - return 0; -} - -class Context { - public: - Context(System* system, Assembler* assembler, Zone* zone, - Compiler::Client* client): - system(system), - assembler(assembler), - arch(assembler->arch()), - zone(zone), - client(client), - stack(0), - locals(0), - saved(0), - predecessor(0), - logicalCode(0), - registerStart(maskStart(registerMask(arch))), - registerLimit(maskLimit(registerMask(arch))), - generalRegisterStart(maskStart(arch->generalRegisterMask())), - generalRegisterLimit(maskLimit(arch->generalRegisterMask())), - floatRegisterStart(maskStart(arch->floatRegisterMask())), - floatRegisterLimit(maskLimit(arch->floatRegisterMask())), - registerResources - (static_cast - (zone->allocate(sizeof(RegisterResource) * registerLimit))), - frameResources(0), - acquiredResources(0), - firstConstant(0), - lastConstant(0), - machineCode(0), - firstEvent(0), - lastEvent(0), - forkState(0), - subroutine(0), - firstBlock(0), - logicalIp(-1), - constantCount(0), - logicalCodeLength(0), - parameterFootprint(0), - localFootprint(0), - machineCodeSize(0), - alignedFrameSize(0), - availableGeneralRegisterCount(generalRegisterLimit - generalRegisterStart) - { - for (unsigned i = generalRegisterStart; i < generalRegisterLimit; ++i) { - new (registerResources + i) RegisterResource(arch->reserved(i)); - - if (registerResources[i].reserved) { - -- availableGeneralRegisterCount; - } - } - for (unsigned i = floatRegisterStart; i < floatRegisterLimit; ++i) { - new (registerResources + i) RegisterResource(arch->reserved(i)); - } - } - - System* system; - Assembler* assembler; - Assembler::Architecture* arch; - Zone* zone; - Compiler::Client* client; - Stack* stack; - Local* locals; - Cell* saved; - Event* predecessor; - LogicalInstruction** logicalCode; - uint8_t registerStart; - uint8_t registerLimit; - uint8_t generalRegisterStart; - uint8_t generalRegisterLimit; - uint8_t floatRegisterStart; - uint8_t floatRegisterLimit; - RegisterResource* registerResources; - FrameResource* frameResources; - Resource* acquiredResources; - ConstantPoolNode* firstConstant; - ConstantPoolNode* lastConstant; - uint8_t* machineCode; - Event* firstEvent; - Event* lastEvent; - ForkState* forkState; - MySubroutine* subroutine; - Block* firstBlock; - int logicalIp; - unsigned constantCount; - unsigned logicalCodeLength; - unsigned parameterFootprint; - unsigned localFootprint; - unsigned machineCodeSize; - unsigned alignedFrameSize; - unsigned availableGeneralRegisterCount; -}; - -unsigned -RegisterResource::index(Context* c) -{ - return this - c->registerResources; -} - -unsigned -FrameResource::index(Context* c) -{ - return this - c->frameResources; -} - -class PoolPromise: public Promise { - public: - PoolPromise(Context* c, int key): c(c), key(key) { } - - virtual int64_t value() { - if (resolved()) { - return reinterpret_cast - (c->machineCode + pad(c->machineCodeSize, TargetBytesPerWord) - + (key * TargetBytesPerWord)); - } - - abort(c); - } - - virtual bool resolved() { - return c->machineCode != 0; - } - - Context* c; - int key; -}; - -class CodePromise: public Promise { - public: - CodePromise(Context* c, CodePromise* next): - c(c), offset(0), next(next) - { } - - CodePromise(Context* c, Promise* offset): - c(c), offset(offset), next(0) - { } - - virtual int64_t value() { - if (resolved()) { - return reinterpret_cast(c->machineCode + offset->value()); - } - - abort(c); - } - - virtual bool resolved() { - return c->machineCode != 0 and offset and offset->resolved(); - } - - Context* c; - Promise* offset; - CodePromise* next; -}; - -unsigned -machineOffset(Context* c, int logicalIp) -{ - return c->logicalCode[logicalIp]->machineOffset->value(); -} - -class IpPromise: public Promise { - public: - IpPromise(Context* c, int logicalIp): - c(c), - logicalIp(logicalIp) - { } - - virtual int64_t value() { - if (resolved()) { - return reinterpret_cast - (c->machineCode + machineOffset(c, logicalIp)); - } - - abort(c); - } - - virtual bool resolved() { - return c->machineCode != 0 - and c->logicalCode[logicalIp]->machineOffset->resolved(); - } - - Context* c; - int logicalIp; -}; - -inline void NO_RETURN -abort(Context* c) -{ - abort(c->system); -} - -#ifndef NDEBUG -inline void -assert(Context* c, bool v) -{ - assert(c->system, v); -} -#endif // not NDEBUG - -inline void -expect(Context* c, bool v) -{ - expect(c->system, v); -} - -unsigned -count(Cell* c) -{ - unsigned count = 0; - while (c) { - ++ count; - c = c->next; - } - return count; -} - -Cell* -cons(Context* c, void* value, Cell* next) -{ - return new (c->zone) Cell(next, value); -} - -Cell* -reverseDestroy(Cell* cell) -{ - Cell* previous = 0; - while (cell) { - Cell* next = cell->next; - cell->next = previous; - previous = cell; - cell = next; - } - return previous; -} - -class StubReadPair { - public: - Value* value; - StubRead* read; -}; - -class JunctionState { - public: - JunctionState(unsigned frameFootprint): frameFootprint(frameFootprint) { } - - unsigned frameFootprint; - StubReadPair reads[0]; -}; - -class Link { - public: - Link(Event* predecessor, Link* nextPredecessor, Event* successor, - Link* nextSuccessor, ForkState* forkState): - predecessor(predecessor), nextPredecessor(nextPredecessor), - successor(successor), nextSuccessor(nextSuccessor), forkState(forkState), - junctionState(0) - { } - - Event* predecessor; - Link* nextPredecessor; - Event* successor; - Link* nextSuccessor; - ForkState* forkState; - JunctionState* junctionState; -}; - -Link* -link(Context* c, Event* predecessor, Link* nextPredecessor, Event* successor, - Link* nextSuccessor, ForkState* forkState) -{ - return new(c->zone) Link - (predecessor, nextPredecessor, successor, nextSuccessor, forkState); -} - -unsigned -countPredecessors(Link* link) -{ - unsigned c = 0; - for (; link; link = link->nextPredecessor) ++ c; - return c; -} - -Link* -lastPredecessor(Link* link) -{ - while (link->nextPredecessor) link = link->nextPredecessor; - return link; -} - -unsigned -countSuccessors(Link* link) -{ - unsigned c = 0; - for (; link; link = link->nextSuccessor) ++ c; - return c; -} - -class Event { - public: - Event(Context* c): - next(0), stackBefore(c->stack), localsBefore(c->locals), - stackAfter(0), localsAfter(0), promises(0), reads(0), - junctionSites(0), snapshots(0), predecessors(0), successors(0), - visitLinks(0), block(0), logicalInstruction(c->logicalCode[c->logicalIp]), - readCount(0) - { } - - virtual const char* name() = 0; - - virtual void compile(Context* c) = 0; - - virtual bool isBranch() { return false; } - - virtual bool allExits() { return false; } - - Event* next; - Stack* stackBefore; - Local* localsBefore; - Stack* stackAfter; - Local* localsAfter; - CodePromise* promises; - Read* reads; - Site** junctionSites; - Snapshot* snapshots; - Link* predecessors; - Link* successors; - Cell* visitLinks; - Block* block; - LogicalInstruction* logicalInstruction; - unsigned readCount; -}; - -unsigned -totalFrameSize(Context* c) -{ - return c->alignedFrameSize - + c->arch->frameHeaderSize() - + c->arch->argumentFootprint(c->parameterFootprint); -} - -int -frameIndex(Context* c, int localIndex) -{ - assert(c, localIndex >= 0); - - 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) -{ - assert(c, frameIndex < totalFrameSize(c)); - - return (frameIndex + c->arch->frameFooterSize()) * TargetBytesPerWord; -} - -unsigned -offsetToFrameIndex(Context* c, unsigned offset) -{ - assert(c, static_cast - ((offset / TargetBytesPerWord) - c->arch->frameFooterSize()) >= 0); - assert(c, ((offset / TargetBytesPerWord) - c->arch->frameFooterSize()) - < totalFrameSize(c)); - - return (offset / TargetBytesPerWord) - c->arch->frameFooterSize(); -} - -unsigned -frameBase(Context* c) -{ - return c->alignedFrameSize - - c->arch->frameReturnAddressSize() - - c->arch->frameFooterSize() - + c->arch->frameHeaderSize(); -} - -class FrameIterator { - public: - class Element { - public: - Element(Value* value, unsigned localIndex): - value(value), localIndex(localIndex) - { } - - Value* const value; - const unsigned localIndex; - }; - - FrameIterator(Context* c, Stack* stack, Local* locals, - bool includeEmpty = false): - stack(stack), locals(locals), localIndex(c->localFootprint - 1), - includeEmpty(includeEmpty) - { } - - bool hasMore() { - if (not includeEmpty) { - while (stack and stack->value == 0) stack = stack->next; - - while (localIndex >= 0 and locals[localIndex].value == 0) -- localIndex; - } - - return stack != 0 or localIndex >= 0; - } - - Element next(Context* c) { - Value* v; - unsigned li; - if (stack) { - Stack* s = stack; - v = s->value; - li = s->index + c->localFootprint; - stack = stack->next; - } else { - Local* l = locals + localIndex; - v = l->value; - li = localIndex; - -- localIndex; - } - return Element(v, li); - } - - Stack* stack; - Local* locals; - int localIndex; - bool includeEmpty; -}; - -int -frameIndex(Context* c, FrameIterator::Element* element) -{ - return frameIndex(c, element->localIndex); -} - -class SiteIterator { - public: - SiteIterator(Context* c, Value* v, bool includeBuddies = true, - bool includeNextWord = true): - c(c), - originalValue(v), - currentValue(v), - includeBuddies(includeBuddies), - includeNextWord(includeNextWord), - pass(0), - next_(findNext(&(v->sites))), - previous(0) - { } - - Site** findNext(Site** p) { - while (true) { - if (*p) { - if (pass == 0 or (*p)->registerSize(c) > TargetBytesPerWord) { - return p; - } else { - p = &((*p)->next); - } - } else { - if (includeBuddies) { - Value* v = currentValue->buddy; - if (v != originalValue) { - currentValue = v; - p = &(v->sites); - continue; - } - } - - if (includeNextWord and pass == 0) { - Value* v = originalValue->nextWord; - if (v != originalValue) { - pass = 1; - originalValue = v; - currentValue = v; - p = &(v->sites); - continue; - } - } - - return 0; - } - } - } - - bool hasMore() { - if (previous) { - next_ = findNext(&((*previous)->next)); - previous = 0; - } - return next_ != 0; - } - - Site* next() { - previous = next_; - return *previous; - } - - void remove(Context* c) { - (*previous)->release(c, originalValue); - *previous = (*previous)->next; - next_ = findNext(previous); - previous = 0; - } - - Context* c; - Value* originalValue; - Value* currentValue; - bool includeBuddies; - bool includeNextWord; - uint8_t pass; - Site** next_; - Site** previous; -}; - -bool -hasSite(Context* c, Value* v) -{ - SiteIterator it(c, v); - return it.hasMore(); -} - -bool -findSite(Context*, Value* v, Site* site) -{ - for (Site* s = v->sites; s; s = s->next) { - if (s == site) return true; - } - return false; -} - -bool -uniqueSite(Context* c, Value* v, Site* s) -{ - SiteIterator it(c, v); - Site* p UNUSED = it.next(); - if (it.hasMore()) { - // the site is not this word's only site, but if the site is - // shared with the next word, it may be that word's only site - if (v->nextWord != v and s->registerSize(c) > TargetBytesPerWord) { - SiteIterator nit(c, v->nextWord); - Site* p = nit.next(); - if (nit.hasMore()) { - return false; - } else { - return p == s; - } - } else { - return false; - } - } else { - assert(c, p == s); - return true; - } -} - -void -addSite(Context* c, Value* v, Site* s) -{ - if (not findSite(c, v, s)) { - if (DebugSites) { - char buffer[256]; s->toString(c, buffer, 256); - fprintf(stderr, "add site %s to %p\n", buffer, v); - } - s->acquire(c, v); - s->next = v->sites; - v->sites = s; - } -} - -void -removeSite(Context* c, Value* v, Site* s) -{ - for (SiteIterator it(c, v); it.hasMore();) { - if (s == it.next()) { - if (DebugSites) { - char buffer[256]; s->toString(c, buffer, 256); - fprintf(stderr, "remove site %s from %p\n", buffer, v); - } - it.remove(c); - break; - } - } - if (DebugSites) { - fprintf(stderr, "%p has more: %d\n", v, hasSite(c, v)); - } - assert(c, not findSite(c, v, s)); -} - -void -clearSites(Context* c, Value* v) -{ - if (DebugSites) { - fprintf(stderr, "clear sites for %p\n", v); - } - for (SiteIterator it(c, v); it.hasMore();) { - it.next(); - it.remove(c); - } -} - -bool -valid(Read* r) -{ - return r and r->valid(); -} - -#ifndef NDEBUG - -bool -hasBuddy(Context* c, Value* a, Value* b) -{ - if (a == b) { - return true; - } - - int i = 0; - for (Value* p = a->buddy; p != a; p = p->buddy) { - if (p == b) { - return true; - } - if (++i > 1000) { - abort(c); - } - } - return false; -} - -#endif // not NDEBUG - -Read* -live(Context* c UNUSED, Value* v) -{ - assert(c, hasBuddy(c, v->buddy, v)); - - Value* p = v; - do { - if (valid(p->reads)) { - return p->reads; - } - p = p->buddy; - } while (p != v); - - return 0; -} - -Read* -liveNext(Context* c, Value* v) -{ - assert(c, hasBuddy(c, v->buddy, v)); - - Read* r = v->reads->next(c); - if (valid(r)) return r; - - for (Value* p = v->buddy; p != v; p = p->buddy) { - if (valid(p->reads)) return p->reads; - } - - return 0; -} - -unsigned -sitesToString(Context* c, Value* v, char* buffer, unsigned size); - -void -deadWord(Context* c, Value* v) -{ - Value* nextWord = v->nextWord; - assert(c, nextWord != v); - - for (SiteIterator it(c, v, true, false); it.hasMore();) { - Site* s = it.next(); - - if (s->registerSize(c) > TargetBytesPerWord) { - it.remove(c); - addSite(c, nextWord, s); - } - } -} - -void -deadBuddy(Context* c, Value* v, Read* r UNUSED) -{ - assert(c, v->buddy != v); - assert(c, r); - - if (DebugBuddies) { - fprintf(stderr, "remove dead buddy %p from", v); - for (Value* p = v->buddy; p != v; p = p->buddy) { - fprintf(stderr, " %p", p); - } - fprintf(stderr, "\n"); - } - - assert(c, v->buddy); - - Value* next = v->buddy; - v->buddy = v; - Value* p = next; - while (p->buddy != v) p = p->buddy; - p->buddy = next; - - assert(c, p->buddy); - - for (SiteIterator it(c, v, false, false); it.hasMore();) { - Site* s = it.next(); - it.remove(c); - - addSite(c, next, s); - } -} - -void -popRead(Context* c, Event* e UNUSED, Value* v) -{ - assert(c, e == v->reads->event); - - if (DebugReads) { - fprintf(stderr, "pop read %p from %p next %p event %p (%s)\n", - v->reads, v, v->reads->next(c), e, (e ? e->name() : 0)); - } - - v->reads = v->reads->next(c); - - if (not valid(v->reads)) { - Value* nextWord = v->nextWord; - if (nextWord != v) { - if (valid(nextWord->reads)) { - deadWord(c, v); - } else { - deadWord(c, nextWord); - } - } - - Read* r = live(c, v); - if (r) { - deadBuddy(c, v, r); - } else { - clearSites(c, v); - } - } -} - -bool -buddies(Value* a, Value* b) -{ - if (a == b) return true; - for (Value* p = a->buddy; p != a; p = p->buddy) { - if (p == b) return true; - } - return false; -} - -void -addBuddy(Value* original, Value* buddy) -{ - buddy->buddy = original; - Value* p = original; - while (p->buddy != original) p = p->buddy; - p->buddy = buddy; - - if (DebugBuddies) { - fprintf(stderr, "add buddy %p to", buddy); - for (Value* p = buddy->buddy; p != buddy; p = p->buddy) { - fprintf(stderr, " %p", p); - } - fprintf(stderr, "\n"); - } -} - -void -decrementAvailableGeneralRegisterCount(Context* c) -{ - assert(c, c->availableGeneralRegisterCount); - -- c->availableGeneralRegisterCount; - - if (DebugResources) { - fprintf(stderr, "%d registers available\n", - c->availableGeneralRegisterCount); - } -} - -void -incrementAvailableGeneralRegisterCount(Context* c) -{ - ++ c->availableGeneralRegisterCount; - - if (DebugResources) { - fprintf(stderr, "%d registers available\n", - c->availableGeneralRegisterCount); - } -} - -void -increment(Context* c, RegisterResource* r) -{ - if (not r->reserved) { - if (DebugResources) { - char buffer[256]; r->toString(c, buffer, 256); - fprintf(stderr, "increment %s to %d\n", buffer, r->referenceCount + 1); - } - - ++ r->referenceCount; - - if (r->referenceCount == 1 - and ((1 << r->index(c)) & c->arch->generalRegisterMask())) - { - decrementAvailableGeneralRegisterCount(c); - } - } -} - -void -decrement(Context* c, RegisterResource* r) -{ - if (not r->reserved) { - if (DebugResources) { - char buffer[256]; r->toString(c, buffer, 256); - fprintf(stderr, "decrement %s to %d\n", buffer, r->referenceCount - 1); - } - - assert(c, r->referenceCount > 0); - - -- r->referenceCount; - - if (r->referenceCount == 0 - and ((1 << r->index(c)) & c->arch->generalRegisterMask())) - { - incrementAvailableGeneralRegisterCount(c); - } - } -} - -void -freezeResource(Context* c, Resource* r, Value* v) -{ - if (DebugResources) { - char buffer[256]; r->toString(c, buffer, 256); - fprintf(stderr, "%p freeze %s to %d\n", v, buffer, r->freezeCount + 1); - } - - ++ r->freezeCount; -} - -void -RegisterResource::freeze(Context* c, Value* v) -{ - if (not reserved) { - freezeResource(c, this, v); - - if (freezeCount == 1 - and ((1 << index(c)) & c->arch->generalRegisterMask())) - { - decrementAvailableGeneralRegisterCount(c); - } - } -} - -void -FrameResource::freeze(Context* c, Value* v) -{ - freezeResource(c, this, v); -} - -void -thawResource(Context* c, Resource* r, Value* v) -{ - if (not r->reserved) { - if (DebugResources) { - char buffer[256]; r->toString(c, buffer, 256); - fprintf(stderr, "%p thaw %s to %d\n", v, buffer, r->freezeCount - 1); - } - - assert(c, r->freezeCount); - - -- r->freezeCount; - } -} - -void -RegisterResource::thaw(Context* c, Value* v) -{ - if (not reserved) { - thawResource(c, this, v); - - if (freezeCount == 0 - and ((1 << index(c)) & c->arch->generalRegisterMask())) - { - incrementAvailableGeneralRegisterCount(c); - } - } -} - -void -FrameResource::thaw(Context* c, Value* v) -{ - thawResource(c, this, v); -} - -class Target { - public: - static const unsigned MinimumRegisterCost = 0; - static const unsigned MinimumFrameCost = 1; - static const unsigned StealPenalty = 2; - static const unsigned StealUniquePenalty = 4; - static const unsigned IndirectMovePenalty = 4; - static const unsigned LowRegisterPenalty = 10; - static const unsigned Impossible = 20; - - Target(): cost(Impossible) { } - - Target(int index, OperandType type, unsigned cost): - index(index), type(type), cost(cost) - { } - - int16_t index; - OperandType type; - uint8_t cost; -}; - -ValueType -valueType(Context* c, Compiler::OperandType type) -{ - switch (type) { - case Compiler::ObjectType: - case Compiler::AddressType: - case Compiler::IntegerType: - case Compiler::VoidType: - return ValueGeneral; - case Compiler::FloatType: - return ValueFloat; - default: - abort(c); - } -} - -class CostCalculator { - public: - virtual unsigned cost(Context* c, uint8_t typeMask, uint32_t registerMask, - int frameIndex) = 0; -}; - -unsigned -resourceCost(Context* c, Value* v, Resource* r, uint8_t typeMask, - uint32_t registerMask, int frameIndex, - CostCalculator* costCalculator) -{ - if (r->reserved or r->freezeCount or r->referenceCount) { - return Target::Impossible; - } else { - unsigned baseCost = costCalculator ? costCalculator->cost - (c, typeMask, registerMask, frameIndex) : 0; - - if (r->value) { - assert(c, findSite(c, r->value, r->site)); - - if (v and buddies(r->value, v)) { - return baseCost; - } else if (uniqueSite(c, r->value, r->site)) { - return baseCost + Target::StealUniquePenalty; - } else { - return baseCost = Target::StealPenalty; - } - } else { - return baseCost; - } - } -} - -bool -pickRegisterTarget(Context* c, int i, Value* v, uint32_t mask, int* target, - unsigned* cost, CostCalculator* costCalculator = 0) -{ - if ((1 << i) & mask) { - RegisterResource* r = c->registerResources + i; - unsigned myCost = resourceCost - (c, v, r, 1 << RegisterOperand, 1 << i, NoFrameIndex, costCalculator) - + Target::MinimumRegisterCost; - - if ((static_cast(1) << i) == mask) { - *cost = myCost; - return true; - } else if (myCost < *cost) { - *cost = myCost; - *target = i; - } - } - return false; -} - -int -pickRegisterTarget(Context* c, Value* v, uint32_t mask, unsigned* cost, - CostCalculator* costCalculator = 0) -{ - int target = NoRegister; - *cost = Target::Impossible; - - if (mask & c->arch->generalRegisterMask()) { - for (int i = c->generalRegisterLimit - 1; - i >= c->generalRegisterStart; --i) - { - if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { - return i; - } - } - } - - if (mask & c->arch->floatRegisterMask()) { - for (int i = c->floatRegisterStart; - i < static_cast(c->floatRegisterLimit); ++i) - { - if (pickRegisterTarget(c, i, v, mask, &target, cost, costCalculator)) { - return i; - } - } - } - - return target; -} - -Target -pickRegisterTarget(Context* c, Value* v, uint32_t mask, - CostCalculator* costCalculator = 0) -{ - unsigned cost; - int number = pickRegisterTarget(c, v, mask, &cost, costCalculator); - return Target(number, RegisterOperand, cost); -} - -unsigned -frameCost(Context* c, Value* v, int frameIndex, CostCalculator* costCalculator) -{ - return resourceCost - (c, v, c->frameResources + frameIndex, 1 << MemoryOperand, 0, frameIndex, - costCalculator) - + Target::MinimumFrameCost; -} - -Target -pickFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) -{ - Target best; - - Value* p = v; - do { - if (p->home >= 0) { - Target mine - (p->home, MemoryOperand, frameCost(c, v, p->home, costCalculator)); - - if (mine.cost == Target::MinimumFrameCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } - p = p->buddy; - } while (p != v); - - return best; -} - -Target -pickAnyFrameTarget(Context* c, Value* v, CostCalculator* costCalculator) -{ - Target best; - - unsigned count = totalFrameSize(c); - for (unsigned i = 0; i < count; ++i) { - Target mine(i, MemoryOperand, frameCost(c, v, i, costCalculator)); - if (mine.cost == Target::MinimumFrameCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } - - return best; -} - -Target -pickTarget(Context* c, Value* value, const SiteMask& mask, - unsigned registerPenalty, Target best, - CostCalculator* costCalculator) -{ - if (mask.typeMask & (1 << RegisterOperand)) { - Target mine = pickRegisterTarget - (c, value, mask.registerMask, costCalculator); - - mine.cost += registerPenalty; - if (mine.cost == Target::MinimumRegisterCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } - - if (mask.typeMask & (1 << MemoryOperand)) { - if (mask.frameIndex >= 0) { - Target mine(mask.frameIndex, MemoryOperand, - frameCost(c, value, mask.frameIndex, costCalculator)); - if (mine.cost == Target::MinimumFrameCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } else if (mask.frameIndex == AnyFrameIndex) { - Target mine = pickFrameTarget(c, value, costCalculator); - 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, CostCalculator* costCalculator) -{ - unsigned registerPenalty - = (c->availableGeneralRegisterCount > registerReserveCount - ? 0 : Target::LowRegisterPenalty); - - Value* value = read->value; - - uint32_t registerMask - = (value->type == ValueFloat ? ~0 : c->arch->generalRegisterMask()); - - SiteMask mask(~0, registerMask, AnyFrameIndex); - read->intersect(&mask); - - if (value->type == ValueFloat) { - uint32_t floatMask = mask.registerMask & c->arch->floatRegisterMask(); - if (floatMask) { - mask.registerMask = floatMask; - } - } - - Target best; - - Value* successor = read->successor(); - if (successor) { - Read* r = live(c, successor); - if (r) { - SiteMask intersection = mask; - if (r->intersect(&intersection)) { - best = pickTarget - (c, value, intersection, registerPenalty, best, costCalculator); - - if (best.cost <= Target::MinimumFrameCost) { - return best; - } - } - } - } - - best = pickTarget(c, value, mask, registerPenalty, best, costCalculator); - if (best.cost <= Target::MinimumFrameCost) { - return best; - } - - if (intersectRead) { - if (best.cost == Target::Impossible) { - fprintf(stderr, "mask type %d reg %d frame %d\n", - mask.typeMask, mask.registerMask, mask.frameIndex); - abort(c); - } - return best; - } - - { Target mine = pickRegisterTarget(c, value, registerMask, costCalculator); - - mine.cost += registerPenalty; - - if (mine.cost == Target::MinimumRegisterCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } - - { Target mine = pickFrameTarget(c, value, costCalculator); - if (mine.cost == Target::MinimumFrameCost) { - return mine; - } else if (mine.cost < best.cost) { - best = mine; - } - } - - if (best.cost >= Target::StealUniquePenalty - and c->availableGeneralRegisterCount == 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: - best = pickAnyFrameTarget(c, value, costCalculator); - assert(c, best.cost <= 3); - } - - if (best.cost == Target::Impossible) { - abort(c); - } - - return best; -} - -void -acquire(Context* c, Resource* resource, Value* value, Site* site); - -void -release(Context* c, Resource* resource, Value* value, Site* site); - -ConstantSite* -constantSite(Context* c, Promise* value); - -ShiftMaskPromise* -shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) -{ - return new(c->zone) ShiftMaskPromise(base, shift, mask); -} - -CombinedPromise* -combinedPromise(Context* c, Promise* low, Promise* high) -{ - return new(c->zone) CombinedPromise(low, high); -} - -class ConstantSite: public Site { - public: - ConstantSite(Promise* value): value(value) { } - - virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { - if (value->resolved()) { - return vm::snprintf - (buffer, bufferSize, "constant %" LLD, value->value()); - } else { - return vm::snprintf(buffer, bufferSize, "constant unresolved"); - } - } - - virtual unsigned copyCost(Context*, Site* s) { - return (s == this ? 0 : ConstantCopyCost); - } - - virtual bool match(Context*, const SiteMask& mask) { - return mask.typeMask & (1 << ConstantOperand); - } - - virtual bool loneMatch(Context*, const SiteMask&) { - return true; - } - - virtual bool matchNextWord(Context* c, Site* s, unsigned) { - return s->type(c) == ConstantOperand; - } - - virtual OperandType type(Context*) { - return ConstantOperand; - } - - virtual void asAssemblerOperand(Context* c, Site* high, - Assembler::Operand* result) - { - Promise* v = value; - if (high != this) { - v = combinedPromise(c, value, static_cast(high)->value); - } - new (result) Assembler::Constant(v); - } - - virtual Site* copy(Context* c) { - return constantSite(c, value); - } - - virtual Site* copyLow(Context* c) { - return constantSite(c, shiftMaskPromise(c, value, 0, 0xFFFFFFFF)); - } - - virtual Site* copyHigh(Context* c) { - return constantSite(c, shiftMaskPromise(c, value, 32, 0xFFFFFFFF)); - } - - virtual Site* makeNextWord(Context* c, unsigned) { - abort(c); - } - - virtual SiteMask mask(Context*) { - return SiteMask(1 << ConstantOperand, 0, NoFrameIndex); - } - - virtual SiteMask nextWordMask(Context*, unsigned) { - return SiteMask(1 << ConstantOperand, 0, NoFrameIndex); - } - - Promise* value; -}; - -ConstantSite* -constantSite(Context* c, Promise* value) -{ - return new(c->zone) ConstantSite(value); -} - -ResolvedPromise* -resolved(Context* c, int64_t value) -{ - return new(c->zone) ResolvedPromise(value); -} - -ConstantSite* -constantSite(Context* c, int64_t value) -{ - return constantSite(c, resolved(c, value)); -} - -AddressSite* -addressSite(Context* c, Promise* address); - -class AddressSite: public Site { - public: - AddressSite(Promise* address): address(address) { } - - virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { - if (address->resolved()) { - return vm::snprintf - (buffer, bufferSize, "address %" LLD, address->value()); - } else { - return vm::snprintf(buffer, bufferSize, "address unresolved"); - } - } - - virtual unsigned copyCost(Context*, Site* s) { - return (s == this ? 0 : AddressCopyCost); - } - - virtual bool match(Context*, const SiteMask& mask) { - return mask.typeMask & (1 << AddressOperand); - } - - virtual bool loneMatch(Context*, const SiteMask&) { - return false; - } - - virtual bool matchNextWord(Context* c, Site*, unsigned) { - abort(c); - } - - virtual OperandType type(Context*) { - return AddressOperand; - } - - virtual void asAssemblerOperand(Context* c UNUSED, Site* high UNUSED, - Assembler::Operand* result) - { - assert(c, high == this); - - new (result) Assembler::Address(address); - } - - virtual Site* copy(Context* c) { - return addressSite(c, address); - } - - virtual Site* copyLow(Context* c) { - abort(c); - } - - virtual Site* copyHigh(Context* c) { - abort(c); - } - - virtual Site* makeNextWord(Context* c, unsigned) { - abort(c); - } - - virtual SiteMask mask(Context*) { - return SiteMask(1 << AddressOperand, 0, NoFrameIndex); - } - - virtual SiteMask nextWordMask(Context* c, unsigned) { - abort(c); - } - - Promise* address; -}; - -AddressSite* -addressSite(Context* c, Promise* address) -{ - return new(c->zone) AddressSite(address); -} - -RegisterSite* -freeRegisterSite(Context* c, uint32_t mask); - -class RegisterSite: public Site { - public: - RegisterSite(uint32_t mask, int number): - mask_(mask), number(number) - { } - - virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { - if (number != NoRegister) { - return vm::snprintf(buffer, bufferSize, "%p register %d", this, number); - } else { - return vm::snprintf(buffer, bufferSize, - "%p register unacquired (mask %d)", this, mask_); - } - } - - virtual unsigned copyCost(Context* c, Site* s) { - assert(c, number != NoRegister); - - if (s and - (this == s or - (s->type(c) == RegisterOperand - and (static_cast(s)->mask_ & (1 << number))))) - { - return 0; - } else { - return RegisterCopyCost; - } - } - - virtual bool match(Context* c UNUSED, const SiteMask& mask) { - assert(c, number != NoRegister); - - if ((mask.typeMask & (1 << RegisterOperand))) { - return ((static_cast(1) << number) & mask.registerMask); - } else { - return false; - } - } - - virtual bool loneMatch(Context* c UNUSED, const SiteMask& mask) { - assert(c, number != NoRegister); - - if ((mask.typeMask & (1 << RegisterOperand))) { - return ((static_cast(1) << number) == mask.registerMask); - } else { - return false; - } - } - - virtual bool matchNextWord(Context* c, Site* s, unsigned) { - assert(c, number != NoRegister); - - if (s->type(c) != RegisterOperand) { - return false; - } - - RegisterSite* rs = static_cast(s); - unsigned size = rs->registerSize(c); - if (size > TargetBytesPerWord) { - assert(c, number != NoRegister); - return number == rs->number; - } else { - uint32_t mask = c->arch->generalRegisterMask(); - return ((1 << number) & mask) and ((1 << rs->number) & mask); - } - } - - virtual void acquire(Context* c, Value* v) { - Target target; - if (number != NoRegister) { - target = Target(number, RegisterOperand, 0); - } else { - target = pickRegisterTarget(c, v, mask_); - expect(c, target.cost < Target::Impossible); - } - - RegisterResource* resource = c->registerResources + target.index; - local::acquire(c, resource, v, this); - - number = target.index; - } - - virtual void release(Context* c, Value* v) { - assert(c, number != NoRegister); - - local::release(c, c->registerResources + number, v, this); - } - - virtual void freeze(Context* c, Value* v) { - assert(c, number != NoRegister); - - c->registerResources[number].freeze(c, v); - } - - virtual void thaw(Context* c, Value* v) { - assert(c, number != NoRegister); - - c->registerResources[number].thaw(c, v); - } - - virtual bool frozen(Context* c UNUSED) { - assert(c, number != NoRegister); - - return c->registerResources[number].freezeCount != 0; - } - - virtual OperandType type(Context*) { - return RegisterOperand; - } - - virtual void asAssemblerOperand(Context* c UNUSED, Site* high, - Assembler::Operand* result) - { - assert(c, number != NoRegister); - - int highNumber; - if (high != this) { - highNumber = static_cast(high)->number; - assert(c, highNumber != NoRegister); - } else { - highNumber = NoRegister; - } - - new (result) Assembler::Register(number, highNumber); - } - - virtual Site* copy(Context* c) { - uint32_t mask; - - if (number != NoRegister) { - mask = 1 << number; - } else { - mask = mask_; - } - - return freeRegisterSite(c, mask); - } - - virtual Site* copyLow(Context* c) { - abort(c); - } - - virtual Site* copyHigh(Context* c) { - abort(c); - } - - virtual Site* makeNextWord(Context* c, unsigned) { - assert(c, number != NoRegister); - assert(c, ((1 << number) & c->arch->generalRegisterMask())); - - return freeRegisterSite(c, c->arch->generalRegisterMask()); - } - - virtual SiteMask mask(Context* c UNUSED) { - return SiteMask(1 << RegisterOperand, mask_, NoFrameIndex); - } - - virtual SiteMask nextWordMask(Context* c, unsigned) { - assert(c, number != NoRegister); - - if (registerSize(c) > TargetBytesPerWord) { - return SiteMask - (1 << RegisterOperand, number, NoFrameIndex); - } else { - return SiteMask - (1 << RegisterOperand, c->arch->generalRegisterMask(), NoFrameIndex); - } - } - - virtual unsigned registerSize(Context* c) { - assert(c, number != NoRegister); - - if ((1 << number) & c->arch->floatRegisterMask()) { - return c->arch->floatRegisterSize(); - } else { - return TargetBytesPerWord; - } - } - - virtual unsigned registerMask(Context* c UNUSED) { - assert(c, number != NoRegister); - - return 1 << number; - } - - uint32_t mask_; - int number; -}; - -RegisterSite* -registerSite(Context* c, int number) -{ - assert(c, number >= 0); - assert(c, (1 << number) & (c->arch->generalRegisterMask() - | c->arch->floatRegisterMask())); - - return new(c->zone) RegisterSite(1 << number, number); -} - -RegisterSite* -freeRegisterSite(Context* c, uint32_t mask) -{ - return new(c->zone) RegisterSite(mask, NoRegister); -} - -MemorySite* -memorySite(Context* c, int base, int offset = 0, int index = NoRegister, - unsigned scale = 1); - -class MemorySite: public Site { - public: - MemorySite(int base, int offset, int index, unsigned scale): - acquired(false), base(base), offset(offset), index(index), scale(scale) - { } - - virtual unsigned toString(Context*, char* buffer, unsigned bufferSize) { - if (acquired) { - return vm::snprintf(buffer, bufferSize, "memory %d 0x%x %d %d", - base, offset, index, scale); - } else { - return vm::snprintf(buffer, bufferSize, "memory unacquired"); - } - } - - virtual unsigned copyCost(Context* c, Site* s) { - assert(c, acquired); - - if (s and - (this == s or - (s->type(c) == MemoryOperand - and static_cast(s)->base == base - and static_cast(s)->offset == offset - and static_cast(s)->index == index - and static_cast(s)->scale == scale))) - { - return 0; - } else { - return MemoryCopyCost; - } - } - - virtual bool match(Context* c, const SiteMask& mask) { - assert(c, acquired); - - if (mask.typeMask & (1 << MemoryOperand)) { - if (mask.frameIndex >= 0) { - if (base == c->arch->stack()) { - assert(c, index == NoRegister); - return static_cast(frameIndexToOffset(c, mask.frameIndex)) - == offset; - } else { - return false; - } - } else { - return true; - } - } else { - return false; - } - } - - virtual bool loneMatch(Context* c, const SiteMask& mask) { - assert(c, acquired); - - if (mask.typeMask & (1 << MemoryOperand)) { - if (base == c->arch->stack()) { - assert(c, index == NoRegister); - - if (mask.frameIndex == AnyFrameIndex) { - return false; - } else { - return true; - } - } - } - return false; - } - - virtual bool matchNextWord(Context* c, Site* s, unsigned index) { - if (s->type(c) == MemoryOperand) { - MemorySite* ms = static_cast(s); - return ms->base == this->base - and ((index == 1 and ms->offset == static_cast - (this->offset + TargetBytesPerWord)) - or (index == 0 and this->offset == static_cast - (ms->offset + TargetBytesPerWord))) - and ms->index == this->index - and ms->scale == this->scale; - } else { - return false; - } - } - - virtual void acquire(Context* c, Value* v) { - increment(c, c->registerResources + base); - if (index != NoRegister) { - increment(c, c->registerResources + index); - } - - if (base == c->arch->stack()) { - assert(c, index == NoRegister); - assert - (c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); - - local::acquire - (c, c->frameResources + offsetToFrameIndex(c, offset), v, this); - } - - acquired = true; - } - - virtual void release(Context* c, Value* v) { - if (base == c->arch->stack()) { - assert(c, index == NoRegister); - assert - (c, not c->frameResources[offsetToFrameIndex(c, offset)].reserved); - - local::release - (c, c->frameResources + offsetToFrameIndex(c, offset), v, this); - } - - decrement(c, c->registerResources + base); - if (index != NoRegister) { - decrement(c, c->registerResources + index); - } - - acquired = false; - } - - virtual void freeze(Context* c, Value* v) { - if (base == c->arch->stack()) { - c->frameResources[offsetToFrameIndex(c, offset)].freeze(c, v); - } else { - increment(c, c->registerResources + base); - if (index != NoRegister) { - increment(c, c->registerResources + index); - } - } - } - - virtual void thaw(Context* c, Value* v) { - if (base == c->arch->stack()) { - c->frameResources[offsetToFrameIndex(c, offset)].thaw(c, v); - } else { - decrement(c, c->registerResources + base); - if (index != NoRegister) { - decrement(c, c->registerResources + index); - } - } - } - - virtual bool frozen(Context* c) { - return base == c->arch->stack() - and c->frameResources[offsetToFrameIndex(c, offset)].freezeCount != 0; - } - - virtual OperandType type(Context*) { - return MemoryOperand; - } - - virtual void asAssemblerOperand(Context* c UNUSED, Site* high UNUSED, - Assembler::Operand* result) - { - // todo: endianness? - assert(c, high == this - or (static_cast(high)->base == base - and static_cast(high)->offset - == static_cast(offset + TargetBytesPerWord) - and static_cast(high)->index == index - and static_cast(high)->scale == scale)); - - assert(c, acquired); - - new (result) Assembler::Memory(base, offset, index, scale); - } - - virtual Site* copy(Context* c) { - return memorySite(c, base, offset, index, scale); - } - - Site* copyHalf(Context* c, bool add) { - if (add) { - return memorySite(c, base, offset + TargetBytesPerWord, index, scale); - } else { - return copy(c); - } - } - - virtual Site* copyLow(Context* c) { - return copyHalf(c, c->arch->bigEndian()); - } - - virtual Site* copyHigh(Context* c) { - return copyHalf(c, not c->arch->bigEndian()); - } - - virtual Site* makeNextWord(Context* c, unsigned index) { - return memorySite - (c, base, offset + ((index == 1) xor c->arch->bigEndian() - ? TargetBytesPerWord : -TargetBytesPerWord), - this->index, scale); - } - - virtual SiteMask mask(Context* c) { - return SiteMask(1 << MemoryOperand, 0, (base == c->arch->stack()) - ? static_cast(offsetToFrameIndex(c, offset)) - : NoFrameIndex); - } - - virtual SiteMask nextWordMask(Context* c, unsigned index) { - int frameIndex; - if (base == c->arch->stack()) { - assert(c, this->index == NoRegister); - frameIndex = static_cast(offsetToFrameIndex(c, offset)) - + ((index == 1) xor c->arch->bigEndian() ? 1 : -1); - } else { - frameIndex = NoFrameIndex; - } - return SiteMask(1 << MemoryOperand, 0, frameIndex); - } - - virtual bool isVolatile(Context* c) { - return base != c->arch->stack(); - } - - bool acquired; - int base; - int offset; - int index; - unsigned scale; -}; - -MemorySite* -memorySite(Context* c, int base, int offset, int index, unsigned scale) -{ - return new(c->zone) MemorySite(base, offset, index, scale); -} - -MemorySite* -frameSite(Context* c, int frameIndex) -{ - assert(c, frameIndex >= 0); - return memorySite - (c, c->arch->stack(), frameIndexToOffset(c, frameIndex), NoRegister, 0); -} - -void -move(Context* c, Value* value, Site* src, Site* dst); - -unsigned -sitesToString(Context* c, Site* sites, char* buffer, unsigned size) -{ - unsigned total = 0; - for (Site* s = sites; s; s = s->next) { - total += s->toString(c, buffer + total, size - total); - - if (s->next) { - assert(c, size > total + 2); - memcpy(buffer + total, ", ", 2); - total += 2; - } - } - - assert(c, size > total); - buffer[total] = 0; - - return total; -} - -unsigned -sitesToString(Context* c, Value* v, char* buffer, unsigned size) -{ - unsigned total = 0; - Value* p = v; - do { - if (total) { - assert(c, size > total + 2); - memcpy(buffer + total, "; ", 2); - total += 2; - } - - if (p->sites) { - total += vm::snprintf(buffer + total, size - total, "%p has ", p); - total += sitesToString(c, p->sites, buffer + total, size - total); - } else { - total += vm::snprintf(buffer + total, size - total, "%p has nothing", p); - } - - p = p->buddy; - } while (p != v); - - return total; -} - -Site* -pickTargetSite(Context* c, Read* read, bool intersectRead = false, - unsigned registerReserveCount = 0, - CostCalculator* costCalculator = 0) -{ - Target target - (pickTarget - (c, read, intersectRead, registerReserveCount, costCalculator)); - - expect(c, target.cost < Target::Impossible); - - if (target.type == MemoryOperand) { - return frameSite(c, target.index); - } else { - return registerSite(c, target.index); - } -} - -class SingleRead: public Read { - public: - SingleRead(const SiteMask& mask, Value* successor): - next_(0), mask(mask), high_(0), successor_(successor) - { } - - virtual bool intersect(SiteMask* mask, unsigned) { - *mask = local::intersect(*mask, this->mask); - - return true; - } - - virtual Value* high(Context*) { - return high_; - } - - virtual Value* successor() { - return successor_; - } - - virtual bool valid() { - return true; - } - - virtual void append(Context* c UNUSED, Read* r) { - assert(c, next_ == 0); - next_ = r; - } - - virtual Read* next(Context*) { - return next_; - } - - Read* next_; - SiteMask mask; - Value* high_; - Value* successor_; -}; - -SingleRead* -read(Context* c, const SiteMask& mask, Value* successor = 0) -{ - assert(c, (mask.typeMask != 1 << MemoryOperand) or mask.frameIndex >= 0); - - return new(c->zone) SingleRead(mask, successor); -} - -bool -acceptMatch(Context* c, Site* s, Read*, const SiteMask& mask) -{ - return s->match(c, mask); -} - -Site* -pickSourceSite(Context* c, Read* read, Site* target = 0, - unsigned* cost = 0, SiteMask* extraMask = 0, - bool intersectRead = true, bool includeBuddies = true, - bool includeNextWord = true, - bool (*accept)(Context*, Site*, Read*, const SiteMask&) - = acceptMatch) -{ - SiteMask mask; - - if (extraMask) { - mask = intersect(mask, *extraMask); - } - - if (intersectRead) { - read->intersect(&mask); - } - - Site* site = 0; - unsigned copyCost = 0xFFFFFFFF; - for (SiteIterator it(c, read->value, includeBuddies, includeNextWord); - it.hasMore();) - { - Site* s = it.next(); - if (accept(c, s, read, mask)) { - unsigned v = s->copyCost(c, target); - if (v < copyCost) { - site = s; - copyCost = v; - } - } - } - - if (DebugMoves and site and target) { - char srcb[256]; site->toString(c, srcb, 256); - char dstb[256]; target->toString(c, dstb, 256); - fprintf(stderr, "pick source %s to %s for %p cost %d\n", - srcb, dstb, read->value, copyCost); - } - - if (cost) *cost = copyCost; - return site; -} - -Site* -maybeMove(Context* c, Read* read, bool intersectRead, bool includeNextWord, - unsigned registerReserveCount = 0) -{ - Value* value = read->value; - unsigned size = value == value->nextWord ? TargetBytesPerWord : 8; - - class MyCostCalculator: public CostCalculator { - public: - MyCostCalculator(Value* value, unsigned size, bool includeNextWord): - value(value), - size(size), - includeNextWord(includeNextWord) - { } - - virtual unsigned cost(Context* c, uint8_t typeMask, uint32_t registerMask, - int frameIndex) - { - uint8_t srcTypeMask; - uint64_t srcRegisterMask; - uint8_t tmpTypeMask; - uint64_t tmpRegisterMask; - c->arch->planMove - (size, &srcTypeMask, &srcRegisterMask, - &tmpTypeMask, &tmpRegisterMask, - typeMask, registerMask); - - SiteMask srcMask(srcTypeMask, srcRegisterMask, AnyFrameIndex); - SiteMask dstMask(typeMask, registerMask, frameIndex); - for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { - Site* s = it.next(); - if (s->match(c, srcMask) or s->match(c, dstMask)) { - return 0; - } - } - - return Target::IndirectMovePenalty; - } - - Value* value; - unsigned size; - bool includeNextWord; - } costCalculator(value, size, includeNextWord); - - Site* dst = pickTargetSite - (c, read, intersectRead, registerReserveCount, &costCalculator); - - uint8_t srcTypeMask; - uint64_t srcRegisterMask; - uint8_t tmpTypeMask; - uint64_t tmpRegisterMask; - c->arch->planMove - (size, &srcTypeMask, &srcRegisterMask, - &tmpTypeMask, &tmpRegisterMask, - 1 << dst->type(c), dst->registerMask(c)); - - SiteMask srcMask(srcTypeMask, srcRegisterMask, AnyFrameIndex); - unsigned cost = 0xFFFFFFFF; - Site* src = 0; - for (SiteIterator it(c, value, true, includeNextWord); it.hasMore();) { - Site* s = it.next(); - unsigned v = s->copyCost(c, dst); - if (v == 0) { - src = s; - cost = 0; - break; - } - if (not s->match(c, srcMask)) { - v += CopyPenalty; - } - if (v < cost) { - src = s; - cost = v; - } - } - - if (cost) { - if (DebugMoves) { - char srcb[256]; src->toString(c, srcb, 256); - char dstb[256]; dst->toString(c, dstb, 256); - fprintf(stderr, "maybe move %s to %s for %p to %p\n", - srcb, dstb, value, value); - } - - src->freeze(c, value); - - addSite(c, value, dst); - - src->thaw(c, value); - - if (not src->match(c, srcMask)) { - src->freeze(c, value); - dst->freeze(c, value); - - SiteMask tmpMask(tmpTypeMask, tmpRegisterMask, AnyFrameIndex); - SingleRead tmpRead(tmpMask, 0); - tmpRead.value = value; - tmpRead.successor_ = value; - - Site* tmp = pickTargetSite(c, &tmpRead, true); - - addSite(c, value, tmp); - - move(c, value, src, tmp); - - dst->thaw(c, value); - src->thaw(c, value); - - src = tmp; - } - - move(c, value, src, dst); - } - - return dst; -} - -Site* -maybeMove(Context* c, Value* v, const SiteMask& mask, bool intersectMask, - bool includeNextWord, unsigned registerReserveCount = 0) -{ - SingleRead read(mask, 0); - read.value = v; - read.successor_ = v; - - return maybeMove - (c, &read, intersectMask, includeNextWord, registerReserveCount); -} - -Site* -pickSiteOrMove(Context* c, Read* read, bool intersectRead, - bool includeNextWord, unsigned registerReserveCount = 0) -{ - Site* s = pickSourceSite - (c, read, 0, 0, 0, intersectRead, true, includeNextWord); - - if (s) { - return s; - } else { - return maybeMove - (c, read, intersectRead, includeNextWord, registerReserveCount); - } -} - -Site* -pickSiteOrMove(Context* c, Value* v, const SiteMask& mask, bool intersectMask, - bool includeNextWord, unsigned registerReserveCount = 0) -{ - SingleRead read(mask, 0); - read.value = v; - read.successor_ = v; - - return pickSiteOrMove - (c, &read, intersectMask, includeNextWord, registerReserveCount); -} - -void -steal(Context* c, Resource* r, Value* thief) -{ - if (DebugResources) { - char resourceBuffer[256]; r->toString(c, resourceBuffer, 256); - char siteBuffer[1024]; sitesToString(c, r->value, siteBuffer, 1024); - fprintf(stderr, "%p steal %s from %p (%s)\n", - thief, resourceBuffer, r->value, siteBuffer); - } - - if ((not (thief and buddies(thief, r->value)) - and uniqueSite(c, r->value, r->site))) - { - r->site->freeze(c, r->value); - - maybeMove(c, live(c, r->value), false, true, StealRegisterReserveCount); - - r->site->thaw(c, r->value); - } - - removeSite(c, r->value, r->site); -} - -void -acquire(Context* c, Resource* resource, Value* value, Site* site) -{ - assert(c, value); - assert(c, site); - - if (not resource->reserved) { - if (DebugResources) { - char buffer[256]; resource->toString(c, buffer, 256); - fprintf(stderr, "%p acquire %s\n", value, buffer); - } - - if (resource->value) { - assert(c, findSite(c, resource->value, resource->site)); - assert(c, not findSite(c, value, resource->site)); - - steal(c, resource, value); - } - - if (c->acquiredResources) { - c->acquiredResources->previousAcquired = resource; - resource->nextAcquired = c->acquiredResources; - } - c->acquiredResources = resource; - - resource->value = value; - resource->site = site; - } -} - -void -release(Context* c, Resource* resource, Value* value UNUSED, Site* site UNUSED) -{ - if (not resource->reserved) { - if (DebugResources) { - char buffer[256]; resource->toString(c, buffer, 256); - fprintf(stderr, "%p release %s\n", resource->value, buffer); - } - - assert(c, resource->value); - assert(c, resource->site); - - assert(c, buddies(resource->value, value)); - assert(c, site == resource->site); - - Resource* next = resource->nextAcquired; - if (next) { - next->previousAcquired = resource->previousAcquired; - resource->nextAcquired = 0; - } - - Resource* previous = resource->previousAcquired; - if (previous) { - previous->nextAcquired = next; - resource->previousAcquired = 0; - } else { - assert(c, c->acquiredResources == resource); - c->acquiredResources = next; - } - - resource->value = 0; - resource->site = 0; - } -} - -SiteMask -generalRegisterMask(Context* c) -{ - return SiteMask - (1 << RegisterOperand, c->arch->generalRegisterMask(), NoFrameIndex); -} - -SiteMask -generalRegisterOrConstantMask(Context* c) -{ - return SiteMask - ((1 << RegisterOperand) | (1 << ConstantOperand), - c->arch->generalRegisterMask(), NoFrameIndex); -} - -SiteMask -fixedRegisterMask(int number) -{ - return SiteMask(1 << RegisterOperand, 1 << number, NoFrameIndex); -} - -class MultiRead: public Read { - public: - MultiRead(): - reads(0), lastRead(0), firstTarget(0), lastTarget(0), visited(false) - { } - - virtual bool intersect(SiteMask* mask, unsigned depth) { - if (depth > 0) { - // short-circuit recursion to avoid poor performance in - // deeply-nested branches - return reads != 0; - } - - bool result = false; - if (not visited) { - visited = true; - for (Cell** cell = &reads; *cell;) { - Read* r = static_cast((*cell)->value); - bool valid = r->intersect(mask, depth + 1); - if (valid) { - result = true; - cell = &((*cell)->next); - } else { - *cell = (*cell)->next; - } - } - visited = false; - } - return result; - } - - virtual Value* successor() { - return 0; - } - - virtual bool valid() { - bool result = false; - if (not visited) { - visited = true; - for (Cell** cell = &reads; *cell;) { - Read* r = static_cast((*cell)->value); - if (r->valid()) { - result = true; - cell = &((*cell)->next); - } else { - *cell = (*cell)->next; - } - } - visited = false; - } - return result; - } - - virtual void append(Context* c, Read* r) { - Cell* cell = cons(c, r, 0); - if (lastRead == 0) { - reads = cell; - } else { - lastRead->next = cell; - } - lastRead = cell; - -// fprintf(stderr, "append %p to %p for %p\n", r, lastTarget, this); - - lastTarget->value = r; - } - - virtual Read* next(Context* c) { - abort(c); - } - - void allocateTarget(Context* c) { - Cell* cell = cons(c, 0, 0); - -// fprintf(stderr, "allocate target for %p: %p\n", this, cell); - - if (lastTarget) { - lastTarget->next = cell; - } else { - firstTarget = cell; - } - lastTarget = cell; - } - - Read* nextTarget() { - // fprintf(stderr, "next target for %p: %p\n", this, firstTarget); - - Read* r = static_cast(firstTarget->value); - firstTarget = firstTarget->next; - return r; - } - - Cell* reads; - Cell* lastRead; - Cell* firstTarget; - Cell* lastTarget; - bool visited; -}; - -MultiRead* -multiRead(Context* c) -{ - return new(c->zone) MultiRead; -} - -class StubRead: public Read { - public: - StubRead(): - next_(0), read(0), visited(false), valid_(true) - { } - - virtual bool intersect(SiteMask* mask, unsigned depth) { - if (not visited) { - visited = true; - if (read) { - bool valid = read->intersect(mask, depth); - if (not valid) { - read = 0; - } - } - visited = false; - } - return valid_; - } - - virtual Value* successor() { - return 0; - } - - virtual bool valid() { - return valid_; - } - - virtual void append(Context* c UNUSED, Read* r) { - assert(c, next_ == 0); - next_ = r; - } - - virtual Read* next(Context*) { - return next_; - } - - Read* next_; - Read* read; - bool visited; - bool valid_; -}; - -StubRead* -stubRead(Context* c) -{ - return new(c->zone) StubRead; -} - -Site* -pickSite(Context* c, Value* v, Site* s, unsigned index, bool includeNextWord) -{ - for (SiteIterator it(c, v, true, includeNextWord); it.hasMore();) { - Site* candidate = it.next(); - if (s->matchNextWord(c, candidate, index)) { - return candidate; - } - } - - return 0; -} - -Site* -pickSiteOrMove(Context* c, Value* v, Site* s, unsigned index) -{ - Site* n = pickSite(c, v, s, index, false); - if (n) { - return n; - } - - return maybeMove(c, v, s->nextWordMask(c, index), true, false); -} - -Site* -pickSiteOrMove(Context* c, Value* v, Site* s, Site** low, Site** high) -{ - if (v->wordIndex == 0) { - *low = s; - *high = pickSiteOrMove(c, v->nextWord, s, 1); - return *high; - } else { - *low = pickSiteOrMove(c, v->nextWord, s, 0); - *high = s; - return *low; - } -} - -Site* -pickSiteOrGrow(Context* c, Value* v, Site* s, unsigned index) -{ - Site* n = pickSite(c, v, s, index, false); - if (n) { - return n; - } - - n = s->makeNextWord(c, index); - addSite(c, v, n); - return n; -} - -Site* -pickSiteOrGrow(Context* c, Value* v, Site* s, Site** low, Site** high) -{ - if (v->wordIndex == 0) { - *low = s; - *high = pickSiteOrGrow(c, v->nextWord, s, 1); - return *high; - } else { - *low = pickSiteOrGrow(c, v->nextWord, s, 0); - *high = s; - return *low; - } -} - -bool -isHome(Value* v, int frameIndex) -{ - Value* p = v; - do { - if (p->home == frameIndex) { - return true; - } - p = p->buddy; - } while (p != v); - - return false; -} - -bool -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->availableGeneralRegisterCount > ResolveRegisterReserveCount; - } else { - assert(c, s->match(c, SiteMask(1 << MemoryOperand, 0, AnyFrameIndex))); - - return isHome(read->value, offsetToFrameIndex - (c, static_cast(s)->offset)); - } - } else { - return false; - } -} - -void -move(Context* c, Value* value, Site* src, Site* dst) -{ - if (DebugMoves) { - char srcb[256]; src->toString(c, srcb, 256); - char dstb[256]; dst->toString(c, dstb, 256); - fprintf(stderr, "move %s to %s for %p to %p\n", - srcb, dstb, value, value); - } - - assert(c, findSite(c, value, dst)); - - src->freeze(c, value); - dst->freeze(c, value); - - unsigned srcSize; - unsigned dstSize; - if (value->nextWord == value) { - srcSize = TargetBytesPerWord; - dstSize = TargetBytesPerWord; - } else { - srcSize = src->registerSize(c); - dstSize = dst->registerSize(c); - } - - if (srcSize == dstSize) { - apply(c, Move, srcSize, src, src, dstSize, dst, dst); - } else if (srcSize > TargetBytesPerWord) { - Site* low, *high, *other = pickSiteOrGrow(c, value, dst, &low, &high); - other->freeze(c, value->nextWord); - - apply(c, Move, srcSize, src, src, srcSize, low, high); - - other->thaw(c, value->nextWord); - } else { - Site* low, *high, *other = pickSiteOrMove(c, value, src, &low, &high); - other->freeze(c, value->nextWord); - - apply(c, Move, dstSize, low, high, dstSize, dst, dst); - - other->thaw(c, value->nextWord); - } - - dst->thaw(c, value); - src->thaw(c, value); -} - -void -asAssemblerOperand(Context* c, Site* low, Site* high, - Assembler::Operand* result) -{ - low->asAssemblerOperand(c, high, result); -} - -class OperandUnion: public Assembler::Operand { - // must be large enough and aligned properly to hold any operand - // type (we'd use an actual union type here, except that classes - // with constructors cannot be used in a union): - uintptr_t padding[4]; -}; - -void -apply(Context* c, UnaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High) -{ - assert(c, s1Low->type(c) == s1High->type(c)); - - OperandType s1Type = s1Low->type(c); - OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); - - c->assembler->apply(op, s1Size, s1Type, &s1Union); -} - -void -apply(Context* c, BinaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High, - unsigned s2Size, Site* s2Low, Site* s2High) -{ - assert(c, s1Low->type(c) == s1High->type(c)); - assert(c, s2Low->type(c) == s2High->type(c)); - - OperandType s1Type = s1Low->type(c); - OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); - - OperandType s2Type = s2Low->type(c); - OperandUnion s2Union; asAssemblerOperand(c, s2Low, s2High, &s2Union); - - c->assembler->apply(op, s1Size, s1Type, &s1Union, - s2Size, s2Type, &s2Union); -} - -void -apply(Context* c, TernaryOperation op, - unsigned s1Size, Site* s1Low, Site* s1High, - unsigned s2Size, Site* s2Low, Site* s2High, - unsigned s3Size, Site* s3Low, Site* s3High) -{ - assert(c, s1Low->type(c) == s1High->type(c)); - assert(c, s2Low->type(c) == s2High->type(c)); - assert(c, s3Low->type(c) == s3High->type(c)); - - OperandType s1Type = s1Low->type(c); - OperandUnion s1Union; asAssemblerOperand(c, s1Low, s1High, &s1Union); - - OperandType s2Type = s2Low->type(c); - OperandUnion s2Union; asAssemblerOperand(c, s2Low, s2High, &s2Union); - - OperandType s3Type = s3Low->type(c); - OperandUnion s3Union; asAssemblerOperand(c, s3Low, s3High, &s3Union); - - c->assembler->apply(op, s1Size, s1Type, &s1Union, - s2Size, s2Type, &s2Union, - s3Size, s3Type, &s3Union); -} - -void -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)); - } - - r->value = v; - if (e) { - r->event = e; - r->eventNext = e->reads; - e->reads = r; - ++ e->readCount; - } - - if (v->lastRead) { - // if (DebugReads) { - // fprintf(stderr, "append %p to %p for %p\n", r, v->lastRead, v); - // } - - v->lastRead->append(c, r); - } else { - v->reads = r; - } - v->lastRead = r; -} - -void -addRead(Context* c, Event* e, Value* v, const SiteMask& mask, - Value* successor = 0) -{ - addRead(c, e, v, read(c, mask, successor)); -} - -void -addReads(Context* c, Event* e, Value* v, unsigned size, - const SiteMask& lowMask, Value* lowSuccessor, - const SiteMask& highMask, Value* highSuccessor) -{ - SingleRead* r = read(c, lowMask, lowSuccessor); - addRead(c, e, v, r); - if (size > TargetBytesPerWord) { - r->high_ = v->nextWord; - addRead(c, e, v->nextWord, highMask, highSuccessor); - } -} - -void -addReads(Context* c, Event* e, Value* v, unsigned size, - const SiteMask& lowMask, const SiteMask& highMask) -{ - addReads(c, e, v, size, lowMask, 0, highMask, 0); -} - -void -clean(Context* c, Value* v, unsigned popIndex) -{ - for (SiteIterator it(c, v); it.hasMore();) { - Site* s = it.next(); - if (not (s->match(c, SiteMask(1 << MemoryOperand, 0, AnyFrameIndex)) - and offsetToFrameIndex - (c, static_cast(s)->offset) - >= popIndex)) - { - 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 offset 0x%x\n", - buffer, v, offsetToFrameIndex - (c, static_cast(s)->offset), - frameIndexToOffset(c, popIndex)); - } - it.remove(c); - } - } -} - -void -clean(Context* c, Event* e, Stack* stack, Local* locals, Read* reads, - unsigned popIndex) -{ - for (FrameIterator it(c, stack, locals); it.hasMore();) { - FrameIterator::Element e = it.next(c); - clean(c, e.value, popIndex); - } - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, e, r->value); - } -} - -CodePromise* -codePromise(Context* c, Event* e) -{ - return e->promises = new(c->zone) CodePromise(c, e->promises); -} - -CodePromise* -codePromise(Context* c, Promise* offset) -{ - return new (c->zone) CodePromise(c, offset); -} - -void -append(Context* c, Event* e); - -void -saveLocals(Context* c, Event* e) -{ - for (unsigned li = 0; li < c->localFootprint; ++li) { - Local* local = e->localsBefore + li; - if (local->value) { - if (DebugReads) { - fprintf(stderr, "local save read %p at %d of %d\n", - local->value, local::frameIndex(c, li), totalFrameSize(c)); - } - - addRead(c, e, local->value, SiteMask - (1 << MemoryOperand, 0, local::frameIndex(c, li))); - } - } -} - -class CallEvent: public Event { - public: - CallEvent(Context* c, Value* address, unsigned flags, - TraceHandler* traceHandler, Value* result, unsigned resultSize, - Stack* argumentStack, unsigned argumentCount, - unsigned stackArgumentFootprint): - Event(c), - address(address), - traceHandler(traceHandler), - result(result), - returnAddressSurrogate(0), - framePointerSurrogate(0), - popIndex(0), - stackArgumentIndex(0), - flags(flags), - resultSize(resultSize), - stackArgumentFootprint(stackArgumentFootprint) - { - uint32_t registerMask = c->arch->generalRegisterMask(); - - if (argumentCount) { - assert(c, (flags & Compiler::TailJump) == 0); - assert(c, stackArgumentFootprint == 0); - - Stack* s = argumentStack; - unsigned index = 0; - unsigned argumentIndex = 0; - - while (true) { - unsigned footprint - = (argumentIndex + 1 < argumentCount - and s->value->nextWord == s->next->value) - ? 2 : 1; - - if (index % (c->arch->argumentAlignment() ? footprint : 1)) { - ++ index; - } - - SiteMask targetMask; - if (index + (c->arch->argumentRegisterAlignment() ? footprint : 1) - <= c->arch->argumentRegisterCount()) - { - int number = c->arch->argumentRegister(index); - - if (DebugReads) { - fprintf(stderr, "reg %d arg read %p\n", number, s->value); - } - - targetMask = fixedRegisterMask(number); - registerMask &= ~(1 << number); - } else { - if (index < c->arch->argumentRegisterCount()) { - index = c->arch->argumentRegisterCount(); - } - - unsigned frameIndex = index - c->arch->argumentRegisterCount(); - - if (DebugReads) { - fprintf(stderr, "stack %d arg read %p\n", frameIndex, s->value); - } - - targetMask = SiteMask(1 << MemoryOperand, 0, frameIndex); - } - - addRead(c, this, s->value, targetMask); - - ++ index; - - if ((++ argumentIndex) < argumentCount) { - s = s->next; - } else { - break; - } - } - } - - if (DebugReads) { - fprintf(stderr, "address read %p\n", address); - } - - { bool thunk; - uint8_t typeMask; - uint64_t planRegisterMask; - c->arch->plan - ((flags & Compiler::Aligned) ? AlignedCall : Call, TargetBytesPerWord, - &typeMask, &planRegisterMask, &thunk); - - assert(c, not thunk); - - addRead(c, this, address, SiteMask - (typeMask, registerMask & planRegisterMask, AnyFrameIndex)); - } - - Stack* stack = stackBefore; - - if (stackArgumentFootprint) { - RUNTIME_ARRAY(Value*, arguments, stackArgumentFootprint); - for (int i = stackArgumentFootprint - 1; i >= 0; --i) { - Value* v = stack->value; - stack = stack->next; - - if ((TargetBytesPerWord == 8 - and (v == 0 or (i >= 1 and stack->value == 0))) - or (TargetBytesPerWord == 4 and v->nextWord != v)) - { - assert(c, TargetBytesPerWord == 8 or v->nextWord == stack->value); - - RUNTIME_ARRAY_BODY(arguments)[i--] = stack->value; - stack = stack->next; - } - RUNTIME_ARRAY_BODY(arguments)[i] = v; - } - - 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(); - if (UseFramePointer) { - framePointerIndex = base + c->arch->framePointerOffset(); - } else { - framePointerIndex = -1; - } - - frameOffset = totalFrameSize(c) - - c->arch->argumentFootprint(stackArgumentFootprint); - } else { - returnAddressIndex = -1; - framePointerIndex = -1; - frameOffset = 0; - } - - for (unsigned i = 0; i < stackArgumentFootprint; ++i) { - Value* v = RUNTIME_ARRAY_BODY(arguments)[i]; - if (v) { - int frameIndex = i + frameOffset; - - if (DebugReads) { - fprintf(stderr, "stack arg read %p at %d of %d\n", - v, frameIndex, totalFrameSize(c)); - } - - if (static_cast(frameIndex) == returnAddressIndex) { - returnAddressSurrogate = v; - addRead(c, this, v, generalRegisterMask(c)); - } else if (static_cast(frameIndex) == framePointerIndex) { - framePointerSurrogate = v; - addRead(c, this, v, generalRegisterMask(c)); - } else { - addRead(c, this, v, SiteMask(1 << MemoryOperand, 0, frameIndex)); - } - } - } - } - - 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 = local::frameIndex - (c, stack->index + c->localFootprint); - - if (DebugReads) { - fprintf(stderr, "stack save read %p at %d of %d\n", - stack->value, logicalIndex, totalFrameSize(c)); - } - - addRead(c, this, stack->value, SiteMask - (1 << MemoryOperand, 0, logicalIndex)); - } - - stack = stack->next; - } - - saveLocals(c, this); - } - } - - virtual const char* name() { - return "CallEvent"; - } - - virtual void compile(Context* c) { - UnaryOperation op; - - if (TailCalls and (flags & Compiler::TailJump)) { - if (flags & Compiler::LongJumpOrCall) { - if (flags & Compiler::Aligned) { - op = AlignedLongJump; - } else { - op = LongJump; - } - } else 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::LongJumpOrCall) { - if (flags & Compiler::Aligned) { - op = AlignedLongCall; - } else { - op = LongCall; - } - } else if (flags & Compiler::Aligned) { - op = AlignedCall; - } else { - op = Call; - } - - apply(c, op, TargetBytesPerWord, address->source, address->source); - - if (traceHandler) { - traceHandler->handleTrace(codePromise(c, c->assembler->offset(true)), - 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); - - if (resultSize and live(c, result)) { - addSite(c, result, registerSite(c, c->arch->returnLow())); - if (resultSize > TargetBytesPerWord and live(c, result->nextWord)) { - addSite(c, result->nextWord, registerSite(c, c->arch->returnHigh())); - } - } - } - - virtual bool allExits() { - return (flags & Compiler::TailJump) != 0; - } - - Value* address; - TraceHandler* traceHandler; - Value* result; - Value* returnAddressSurrogate; - Value* framePointerSurrogate; - unsigned popIndex; - unsigned stackArgumentIndex; - unsigned flags; - unsigned resultSize; - unsigned stackArgumentFootprint; -}; - -void -appendCall(Context* c, Value* address, unsigned flags, - TraceHandler* traceHandler, Value* result, unsigned resultSize, - Stack* argumentStack, unsigned argumentCount, - unsigned stackArgumentFootprint) -{ - append(c, new(c->zone) - CallEvent(c, address, flags, traceHandler, result, - resultSize, argumentStack, argumentCount, - 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): - Event(c), value(value) - { - if (value) { - addReads(c, this, value, size, fixedRegisterMask(c->arch->returnLow()), - fixedRegisterMask(c->arch->returnHigh())); - } - } - - virtual const char* name() { - return "ReturnEvent"; - } - - virtual void compile(Context* c) { - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - - if (not unreachable(this)) { - c->assembler->popFrameAndPopArgumentsAndReturn - (c->alignedFrameSize, - c->arch->argumentFootprint(c->parameterFootprint)); - } - } - - Value* value; -}; - -void -appendReturn(Context* c, unsigned size, Value* value) -{ - append(c, new(c->zone) ReturnEvent(c, size, value)); -} - -void -maybeMove(Context* c, BinaryOperation type, unsigned srcSize, - unsigned srcSelectSize, Value* src, unsigned dstSize, Value* dst, - const SiteMask& dstMask) -{ - Read* read = live(c, dst); - bool isStore = read == 0; - - Site* target; - if (dst->target) { - target = dst->target; - } else if (isStore) { - return; - } else { - target = pickTargetSite(c, read); - } - - unsigned cost = src->source->copyCost(c, target); - - if (srcSelectSize < dstSize) cost = 1; - - if (cost) { - // todo: let c->arch->planMove decide this: - bool useTemporary = ((target->type(c) == MemoryOperand - and src->source->type(c) == MemoryOperand) - or (srcSelectSize < dstSize - and target->type(c) != RegisterOperand)); - - src->source->freeze(c, src); - - addSite(c, dst, target); - - src->source->thaw(c, src); - - bool addOffset = srcSize != srcSelectSize - and c->arch->bigEndian() - and src->source->type(c) == MemoryOperand; - - if (addOffset) { - static_cast(src->source)->offset - += (srcSize - srcSelectSize); - } - - target->freeze(c, dst); - - if (target->match(c, dstMask) and not useTemporary) { - if (DebugMoves) { - char srcb[256]; src->source->toString(c, srcb, 256); - char dstb[256]; target->toString(c, dstb, 256); - fprintf(stderr, "move %s to %s for %p to %p\n", - srcb, dstb, src, dst); - } - - src->source->freeze(c, src); - - apply(c, type, min(srcSelectSize, dstSize), src->source, src->source, - dstSize, target, target); - - src->source->thaw(c, src); - } else { - // pick a temporary register which is valid as both a - // destination and a source for the moves we need to perform: - - removeSite(c, dst, target); - - bool thunk; - uint8_t srcTypeMask; - uint64_t srcRegisterMask; - - c->arch->planSource(type, dstSize, &srcTypeMask, &srcRegisterMask, - dstSize, &thunk); - - if (src->type == ValueGeneral) { - srcRegisterMask &= c->arch->generalRegisterMask(); - } - - assert(c, thunk == 0); - assert(c, dstMask.typeMask & srcTypeMask & (1 << RegisterOperand)); - - Site* tmpTarget = freeRegisterSite - (c, dstMask.registerMask & srcRegisterMask); - - src->source->freeze(c, src); - - addSite(c, dst, tmpTarget); - - tmpTarget->freeze(c, dst); - - if (DebugMoves) { - char srcb[256]; src->source->toString(c, srcb, 256); - char dstb[256]; tmpTarget->toString(c, dstb, 256); - fprintf(stderr, "move %s to %s for %p to %p\n", - srcb, dstb, src, dst); - } - - apply(c, type, srcSelectSize, src->source, src->source, - dstSize, tmpTarget, tmpTarget); - - tmpTarget->thaw(c, dst); - - src->source->thaw(c, src); - - if (useTemporary or isStore) { - if (DebugMoves) { - char srcb[256]; tmpTarget->toString(c, srcb, 256); - char dstb[256]; target->toString(c, dstb, 256); - fprintf(stderr, "move %s to %s for %p to %p\n", - srcb, dstb, src, dst); - } - - addSite(c, dst, target); - - tmpTarget->freeze(c, dst); - - apply(c, Move, dstSize, tmpTarget, tmpTarget, dstSize, target, target); - - tmpTarget->thaw(c, dst); - - if (isStore) { - removeSite(c, dst, tmpTarget); - } - } - } - - target->thaw(c, dst); - - if (addOffset) { - static_cast(src->source)->offset - -= (srcSize - srcSelectSize); - } - } else { - target = src->source; - - if (DebugMoves) { - char dstb[256]; target->toString(c, dstb, 256); - fprintf(stderr, "null move in %s for %p to %p\n", dstb, src, dst); - } - } - - if (isStore) { - removeSite(c, dst, target); - } -} - -Site* -pickMatchOrMove(Context* c, Read* r, Site* nextWord, unsigned index, - bool intersectRead) -{ - Site* s = pickSite(c, r->value, nextWord, index, true); - SiteMask mask; - if (intersectRead) { - r->intersect(&mask); - } - if (s and s->match(c, mask)) { - return s; - } - - return pickSiteOrMove - (c, r->value, intersect(mask, nextWord->nextWordMask(c, index)), - true, true); -} - -Site* -pickSiteOrMove(Context* c, Value* src, Value* dst, Site* nextWord, - unsigned index) -{ - if (live(c, dst)) { - Read* read = live(c, src); - Site* s; - if (nextWord) { - s = pickMatchOrMove(c, read, nextWord, index, false); - } else { - s = pickSourceSite(c, read, 0, 0, 0, false, true, true); - - if (s == 0 or s->isVolatile(c)) { - s = maybeMove(c, read, false, true); - } - } - assert(c, s); - - addBuddy(src, dst); - - if (src->source->isVolatile(c)) { - removeSite(c, src, src->source); - } - - return s; - } else { - return 0; - } -} - -Value* -value(Context* c, ValueType type, Site* site = 0, Site* target = 0) -{ - return new(c->zone) Value(site, target, type); -} - -void -grow(Context* c, Value* v) -{ - assert(c, v->nextWord == v); - - Value* next = value(c, v->type); - v->nextWord = next; - next->nextWord = v; - next->wordIndex = 1; -} - -void -split(Context* c, Value* v) -{ - grow(c, v); - for (SiteIterator it(c, v); it.hasMore();) { - Site* s = it.next(); - removeSite(c, v, s); - - addSite(c, v, s->copyLow(c)); - addSite(c, v->nextWord, s->copyHigh(c)); - } -} - -void -maybeSplit(Context* c, Value* v) -{ - if (v->nextWord == v) { - split(c, v); - } -} - -class MoveEvent: public Event { - public: - MoveEvent(Context* c, BinaryOperation type, unsigned srcSize, - unsigned srcSelectSize, Value* src, unsigned dstSize, Value* dst, - const SiteMask& srcLowMask, const SiteMask& srcHighMask): - Event(c), type(type), srcSize(srcSize), srcSelectSize(srcSelectSize), - src(src), dstSize(dstSize), dst(dst) - { - assert(c, srcSelectSize <= srcSize); - - bool noop = srcSelectSize >= dstSize; - - if (dstSize > TargetBytesPerWord) { - grow(c, dst); - } - - if (srcSelectSize > TargetBytesPerWord) { - maybeSplit(c, src); - } - - addReads(c, this, src, srcSelectSize, srcLowMask, noop ? dst : 0, - srcHighMask, - noop and dstSize > TargetBytesPerWord ? dst->nextWord : 0); - } - - virtual const char* name() { - return "MoveEvent"; - } - - virtual void compile(Context* c) { - uint8_t dstTypeMask; - uint64_t dstRegisterMask; - - c->arch->planDestination - (type, - srcSelectSize, - 1 << src->source->type(c), - (static_cast(src->nextWord->source->registerMask(c)) << 32) - | static_cast(src->source->registerMask(c)), - dstSize, - &dstTypeMask, - &dstRegisterMask); - - SiteMask dstLowMask(dstTypeMask, dstRegisterMask, AnyFrameIndex); - SiteMask dstHighMask(dstTypeMask, dstRegisterMask >> 32, AnyFrameIndex); - - if (srcSelectSize >= TargetBytesPerWord - and dstSize >= TargetBytesPerWord - and srcSelectSize >= dstSize) - { - if (dst->target) { - if (dstSize > TargetBytesPerWord) { - if (src->source->registerSize(c) > TargetBytesPerWord) { - apply(c, Move, srcSelectSize, src->source, src->source, - dstSize, dst->target, dst->target); - - if (live(c, dst) == 0) { - removeSite(c, dst, dst->target); - if (dstSize > TargetBytesPerWord) { - removeSite(c, dst->nextWord, dst->nextWord->target); - } - } - } else { - src->nextWord->source->freeze(c, src->nextWord); - - maybeMove(c, Move, TargetBytesPerWord, TargetBytesPerWord, src, - TargetBytesPerWord, dst, dstLowMask); - - src->nextWord->source->thaw(c, src->nextWord); - - maybeMove - (c, Move, TargetBytesPerWord, TargetBytesPerWord, src->nextWord, - TargetBytesPerWord, dst->nextWord, dstHighMask); - } - } else { - maybeMove(c, Move, TargetBytesPerWord, TargetBytesPerWord, src, - TargetBytesPerWord, dst, dstLowMask); - } - } else { - Site* low = pickSiteOrMove(c, src, dst, 0, 0); - if (dstSize > TargetBytesPerWord) { - pickSiteOrMove(c, src->nextWord, dst->nextWord, low, 1); - } - } - } else if (srcSelectSize <= TargetBytesPerWord - and dstSize <= TargetBytesPerWord) - { - maybeMove(c, type, srcSize, srcSelectSize, src, dstSize, dst, - dstLowMask); - } else { - assert(c, srcSize == TargetBytesPerWord); - assert(c, srcSelectSize == TargetBytesPerWord); - - if (dst->nextWord->target or live(c, dst->nextWord)) { - assert(c, dstLowMask.typeMask & (1 << RegisterOperand)); - - Site* low = freeRegisterSite(c, dstLowMask.registerMask); - - src->source->freeze(c, src); - - addSite(c, dst, low); - - low->freeze(c, dst); - - if (DebugMoves) { - char srcb[256]; src->source->toString(c, srcb, 256); - char dstb[256]; low->toString(c, dstb, 256); - fprintf(stderr, "move %s to %s for %p\n", - srcb, dstb, src); - } - - apply(c, Move, TargetBytesPerWord, src->source, src->source, - TargetBytesPerWord, low, low); - - low->thaw(c, dst); - - src->source->thaw(c, src); - - assert(c, dstHighMask.typeMask & (1 << RegisterOperand)); - - Site* high = freeRegisterSite(c, dstHighMask.registerMask); - - low->freeze(c, dst); - - addSite(c, dst->nextWord, high); - - high->freeze(c, dst->nextWord); - - if (DebugMoves) { - char srcb[256]; low->toString(c, srcb, 256); - char dstb[256]; high->toString(c, dstb, 256); - fprintf(stderr, "extend %s to %s for %p %p\n", - srcb, dstb, dst, dst->nextWord); - } - - apply(c, Move, TargetBytesPerWord, low, low, dstSize, low, high); - - high->thaw(c, dst->nextWord); - - low->thaw(c, dst); - } else { - pickSiteOrMove(c, src, dst, 0, 0); - } - } - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - } - - BinaryOperation type; - unsigned srcSize; - unsigned srcSelectSize; - Value* src; - unsigned dstSize; - Value* dst; -}; - -void -appendMove(Context* c, BinaryOperation type, unsigned srcSize, - unsigned srcSelectSize, Value* src, unsigned dstSize, Value* dst) -{ - bool thunk; - uint8_t srcTypeMask; - uint64_t srcRegisterMask; - - c->arch->planSource - (type, srcSelectSize, &srcTypeMask, &srcRegisterMask, dstSize, &thunk); - - assert(c, not thunk); - - append(c, new(c->zone) - MoveEvent - (c, type, srcSize, srcSelectSize, src, dstSize, dst, - SiteMask(srcTypeMask, srcRegisterMask, AnyFrameIndex), - SiteMask(srcTypeMask, srcRegisterMask >> 32, AnyFrameIndex))); -} - -ConstantSite* -findConstantSite(Context* c, Value* v) -{ - for (SiteIterator it(c, v); it.hasMore();) { - Site* s = it.next(); - if (s->type(c) == ConstantOperand) { - return static_cast(s); - } - } - return 0; -} - -void -preserve(Context* c, Value* v, Read* r, Site* s) -{ - s->freeze(c, v); - - maybeMove(c, r, false, true, 0); - - s->thaw(c, v); -} - -Site* -getTarget(Context* c, Value* value, Value* result, const SiteMask& resultMask) -{ - Site* s; - Value* v; - Read* r = liveNext(c, value); - if (value->source->match - (c, static_cast(resultMask)) - and (r == 0 or value->source->loneMatch - (c, static_cast(resultMask)))) - { - s = value->source; - v = value; - if (r and uniqueSite(c, v, s)) { - preserve(c, v, r, s); - } - } else { - SingleRead r(resultMask, 0); - r.value = result; - r.successor_ = result; - s = pickTargetSite(c, &r, true); - v = result; - addSite(c, result, s); - } - - removeSite(c, v, s); - - s->freeze(c, v); - - return s; -} - -void -freezeSource(Context* c, unsigned size, Value* v) -{ - v->source->freeze(c, v); - if (size > TargetBytesPerWord) { - v->nextWord->source->freeze(c, v->nextWord); - } -} - -void -thawSource(Context* c, unsigned size, Value* v) -{ - v->source->thaw(c, v); - if (size > TargetBytesPerWord) { - v->nextWord->source->thaw(c, v->nextWord); - } -} - -class CombineEvent: public Event { - public: - CombineEvent(Context* c, TernaryOperation type, - unsigned firstSize, Value* first, - unsigned secondSize, Value* second, - unsigned resultSize, Value* result, - const SiteMask& firstLowMask, - const SiteMask& firstHighMask, - const SiteMask& secondLowMask, - const SiteMask& secondHighMask): - Event(c), type(type), firstSize(firstSize), first(first), - secondSize(secondSize), second(second), resultSize(resultSize), - result(result) - { - addReads(c, this, first, firstSize, firstLowMask, firstHighMask); - - if (resultSize > TargetBytesPerWord) { - grow(c, result); - } - - bool condensed = c->arch->alwaysCondensed(type); - - addReads(c, this, second, secondSize, - secondLowMask, condensed ? result : 0, - secondHighMask, condensed ? result->nextWord : 0); - } - - virtual const char* name() { - return "CombineEvent"; - } - - virtual void compile(Context* c) { - assert(c, first->source->type(c) == first->nextWord->source->type(c)); - - // if (second->source->type(c) != second->nextWord->source->type(c)) { - // fprintf(stderr, "%p %p %d : %p %p %d\n", - // second, second->source, second->source->type(c), - // second->nextWord, second->nextWord->source, - // second->nextWord->source->type(c)); - // } - - assert(c, second->source->type(c) == second->nextWord->source->type(c)); - - freezeSource(c, firstSize, first); - - uint8_t cTypeMask; - uint64_t cRegisterMask; - - c->arch->planDestination - (type, - firstSize, - 1 << first->source->type(c), - (static_cast(first->nextWord->source->registerMask(c)) << 32) - | static_cast(first->source->registerMask(c)), - secondSize, - 1 << second->source->type(c), - (static_cast(second->nextWord->source->registerMask(c)) << 32) - | static_cast(second->source->registerMask(c)), - resultSize, - &cTypeMask, - &cRegisterMask); - - SiteMask resultLowMask(cTypeMask, cRegisterMask, AnyFrameIndex); - SiteMask resultHighMask(cTypeMask, cRegisterMask >> 32, AnyFrameIndex); - - Site* low = getTarget(c, second, result, resultLowMask); - unsigned lowSize = low->registerSize(c); - Site* high - = (resultSize > lowSize - ? getTarget(c, second->nextWord, result->nextWord, resultHighMask) - : low); - -// fprintf(stderr, "combine %p:%p and %p:%p into %p:%p\n", -// first, first->nextWord, -// second, second->nextWord, -// result, result->nextWord); - - apply(c, type, - firstSize, first->source, first->nextWord->source, - secondSize, second->source, second->nextWord->source, - resultSize, low, high); - - thawSource(c, firstSize, first); - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - - low->thaw(c, second); - if (resultSize > lowSize) { - high->thaw(c, second->nextWord); - } - - if (live(c, result)) { - addSite(c, result, low); - if (resultSize > lowSize and live(c, result->nextWord)) { - addSite(c, result->nextWord, high); - } - } - } - - TernaryOperation type; - unsigned firstSize; - Value* first; - unsigned secondSize; - Value* second; - unsigned resultSize; - Value* result; -}; - -void -removeBuddy(Context* c, Value* v) -{ - if (v->buddy != v) { - if (DebugBuddies) { - fprintf(stderr, "remove buddy %p from", v); - for (Value* p = v->buddy; p != v; p = p->buddy) { - fprintf(stderr, " %p", p); - } - fprintf(stderr, "\n"); - } - - assert(c, v->buddy); - - Value* next = v->buddy; - v->buddy = v; - Value* p = next; - while (p->buddy != v) p = p->buddy; - p->buddy = next; - - assert(c, p->buddy); - - if (not live(c, next)) { - clearSites(c, next); - } - - if (not live(c, v)) { - clearSites(c, v); - } - } -} - -Site* -copy(Context* c, Site* s) -{ - Site* start = 0; - Site* end = 0; - for (; s; s = s->next) { - Site* n = s->copy(c); - if (end) { - end->next = n; - } else { - start = n; - } - end = n; - } - return start; -} - -class Snapshot { - public: - Snapshot(Context* c, Value* value, Snapshot* next): - value(value), buddy(value->buddy), sites(copy(c, value->sites)), next(next) - { } - - Value* value; - Value* buddy; - Site* sites; - Snapshot* next; -}; - -Snapshot* -snapshot(Context* c, Value* value, Snapshot* next) -{ - if (DebugControl) { - char buffer[256]; sitesToString(c, value->sites, buffer, 256); - fprintf(stderr, "snapshot %p buddy %p sites %s\n", - value, value->buddy, buffer); - } - - return new(c->zone) Snapshot(c, value, next); -} - -Snapshot* -makeSnapshots(Context* c, Value* value, Snapshot* next) -{ - next = snapshot(c, value, next); - for (Value* p = value->buddy; p != value; p = p->buddy) { - next = snapshot(c, p, next); - } - return next; -} - -Stack* -stack(Context* c, Value* value, Stack* next) -{ - return new(c->zone) Stack(next ? next->index + 1 : 0, value, next); -} - -Value* -maybeBuddy(Context* c, Value* v); - -Value* -pushWord(Context* c, Value* v) -{ - if (v) { - v = maybeBuddy(c, v); - } - - Stack* s = stack(c, v, c->stack); - - if (DebugFrame) { - fprintf(stderr, "push %p\n", v); - } - - if (v) { - v->home = frameIndex(c, s->index + c->localFootprint); - } - c->stack = s; - - return v; -} - -void -push(Context* c, unsigned footprint, Value* v) -{ - assert(c, footprint); - - bool bigEndian = c->arch->bigEndian(); - - Value* low = v; - - if (bigEndian) { - v = pushWord(c, v); - } - - Value* high; - if (footprint > 1) { - assert(c, footprint == 2); - - if (TargetBytesPerWord == 4) { - maybeSplit(c, low); - high = pushWord(c, low->nextWord); - } else { - high = pushWord(c, 0); - } - } else { - high = 0; - } - - if (not bigEndian) { - v = pushWord(c, v); - } - - if (high) { - v->nextWord = high; - high->nextWord = v; - high->wordIndex = 1; - } -} - -void -popWord(Context* c) -{ - Stack* s = c->stack; - assert(c, s->value == 0 or s->value->home >= 0); - - if (DebugFrame) { - fprintf(stderr, "pop %p\n", s->value); - } - - c->stack = s->next; -} - -Value* -pop(Context* c, unsigned footprint) -{ - assert(c, footprint); - - Stack* s = 0; - - bool bigEndian = c->arch->bigEndian(); - - if (not bigEndian) { - s = c->stack; - } - - if (footprint > 1) { - assert(c, footprint == 2); - -#ifndef NDEBUG - Stack* low; - Stack* high; - if (bigEndian) { - high = c->stack; - low = high->next; - } else { - low = c->stack; - high = low->next; - } - - assert(c, (TargetBytesPerWord == 8 - and low->value->nextWord == low->value and high->value == 0) - or (TargetBytesPerWord == 4 and low->value->nextWord == high->value)); -#endif // not NDEBUG - - popWord(c); - } - - if (bigEndian) { - s = c->stack; - } - - popWord(c); - - return s->value; -} - -Value* -storeLocal(Context* c, unsigned footprint, Value* v, unsigned index, bool copy) -{ - assert(c, index + footprint <= c->localFootprint); - - if (copy) { - unsigned sizeInBytes = sizeof(Local) * c->localFootprint; - Local* newLocals = static_cast(c->zone->allocate(sizeInBytes)); - memcpy(newLocals, c->locals, sizeInBytes); - c->locals = newLocals; - } - - Value* high; - if (footprint > 1) { - assert(c, footprint == 2); - - unsigned highIndex; - unsigned lowIndex; - if (c->arch->bigEndian()) { - highIndex = index + 1; - lowIndex = index; - } else { - lowIndex = index + 1; - highIndex = index; - } - - if (TargetBytesPerWord == 4) { - assert(c, v->nextWord != v); - - high = storeLocal(c, 1, v->nextWord, highIndex, false); - } else { - high = 0; - } - - index = lowIndex; - } else { - high = 0; - } - - v = maybeBuddy(c, v); - - if (high != 0) { - v->nextWord = high; - high->nextWord = v; - high->wordIndex = 1; - } - - Local* local = c->locals + index; - local->value = v; - - if (DebugFrame) { - fprintf(stderr, "store local %p at %d\n", local->value, index); - } - - local->value->home = frameIndex(c, index); - - return v; -} - -Value* -loadLocal(Context* c, unsigned footprint, unsigned index) -{ - assert(c, index + footprint <= c->localFootprint); - - if (footprint > 1) { - assert(c, footprint == 2); - - if (not c->arch->bigEndian()) { - ++ index; - } - } - - assert(c, c->locals[index].value); - assert(c, c->locals[index].value->home >= 0); - - if (DebugFrame) { - fprintf(stderr, "load local %p at %d\n", c->locals[index].value, index); - } - - return c->locals[index].value; -} - -Value* -register_(Context* c, int number) -{ - assert(c, (1 << number) & (c->arch->generalRegisterMask() - | c->arch->floatRegisterMask())); - - Site* s = registerSite(c, number); - ValueType type = ((1 << number) & c->arch->floatRegisterMask()) - ? ValueFloat: ValueGeneral; - - return value(c, type, s, s); -} - -void -appendCombine(Context* c, TernaryOperation type, - unsigned firstSize, Value* first, - unsigned secondSize, Value* second, - unsigned resultSize, Value* result) -{ - bool thunk; - uint8_t firstTypeMask; - uint64_t firstRegisterMask; - uint8_t secondTypeMask; - uint64_t secondRegisterMask; - - c->arch->planSource(type, firstSize, &firstTypeMask, &firstRegisterMask, - secondSize, &secondTypeMask, &secondRegisterMask, - resultSize, &thunk); - - if (thunk) { - Stack* oldStack = c->stack; - - bool threadParameter; - intptr_t handler = c->client->getThunk - (type, firstSize, resultSize, &threadParameter); - - unsigned stackSize = ceiling(secondSize, TargetBytesPerWord) - + ceiling(firstSize, TargetBytesPerWord); - - local::push(c, ceiling(secondSize, TargetBytesPerWord), second); - local::push(c, ceiling(firstSize, TargetBytesPerWord), first); - - if (threadParameter) { - ++ stackSize; - - local::push(c, 1, register_(c, c->arch->thread())); - } - - Stack* argumentStack = c->stack; - c->stack = oldStack; - - appendCall - (c, value(c, ValueGeneral, constantSite(c, handler)), 0, 0, result, - resultSize, argumentStack, stackSize, 0); - } else { - append - (c, new(c->zone) - CombineEvent - (c, type, - firstSize, first, - secondSize, second, - resultSize, result, - SiteMask(firstTypeMask, firstRegisterMask, AnyFrameIndex), - SiteMask(firstTypeMask, firstRegisterMask >> 32, AnyFrameIndex), - SiteMask(secondTypeMask, secondRegisterMask, AnyFrameIndex), - SiteMask(secondTypeMask, secondRegisterMask >> 32, AnyFrameIndex))); - } -} - -class TranslateEvent: public Event { - public: - TranslateEvent(Context* c, BinaryOperation type, unsigned valueSize, - Value* value, unsigned resultSize, Value* result, - const SiteMask& valueLowMask, - const SiteMask& valueHighMask): - Event(c), type(type), valueSize(valueSize), resultSize(resultSize), - value(value), result(result) - { - bool condensed = c->arch->alwaysCondensed(type); - - if (resultSize > TargetBytesPerWord) { - grow(c, result); - } - - addReads(c, this, value, valueSize, valueLowMask, condensed ? result : 0, - valueHighMask, condensed ? result->nextWord : 0); - } - - virtual const char* name() { - return "TranslateEvent"; - } - - virtual void compile(Context* c) { - assert(c, value->source->type(c) == value->nextWord->source->type(c)); - - uint8_t bTypeMask; - uint64_t bRegisterMask; - - c->arch->planDestination - (type, - valueSize, - 1 << value->source->type(c), - (static_cast(value->nextWord->source->registerMask(c)) << 32) - | static_cast(value->source->registerMask(c)), - resultSize, - &bTypeMask, - &bRegisterMask); - - SiteMask resultLowMask(bTypeMask, bRegisterMask, AnyFrameIndex); - SiteMask resultHighMask(bTypeMask, bRegisterMask >> 32, AnyFrameIndex); - - Site* low = getTarget(c, value, result, resultLowMask); - unsigned lowSize = low->registerSize(c); - Site* high - = (resultSize > lowSize - ? getTarget(c, value->nextWord, result->nextWord, resultHighMask) - : low); - - apply(c, type, valueSize, value->source, value->nextWord->source, - resultSize, low, high); - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - - low->thaw(c, value); - if (resultSize > lowSize) { - high->thaw(c, value->nextWord); - } - - if (live(c, result)) { - addSite(c, result, low); - if (resultSize > lowSize and live(c, result->nextWord)) { - addSite(c, result->nextWord, high); - } - } - } - - BinaryOperation type; - unsigned valueSize; - unsigned resultSize; - Value* value; - Value* result; - Read* resultRead; - SiteMask resultLowMask; - SiteMask resultHighMask; -}; - -void -appendTranslate(Context* c, BinaryOperation type, unsigned firstSize, - Value* first, unsigned resultSize, Value* result) -{ - bool thunk; - uint8_t firstTypeMask; - uint64_t firstRegisterMask; - - c->arch->planSource(type, firstSize, &firstTypeMask, &firstRegisterMask, - resultSize, &thunk); - - if (thunk) { - Stack* oldStack = c->stack; - - local::push(c, ceiling(firstSize, TargetBytesPerWord), first); - - Stack* argumentStack = c->stack; - c->stack = oldStack; - - appendCall - (c, value - (c, ValueGeneral, constantSite - (c, c->client->getThunk(type, firstSize, resultSize))), - 0, 0, result, resultSize, argumentStack, - ceiling(firstSize, TargetBytesPerWord), 0); - } else { - append(c, new(c->zone) - TranslateEvent - (c, type, firstSize, first, resultSize, result, - SiteMask(firstTypeMask, firstRegisterMask, AnyFrameIndex), - SiteMask(firstTypeMask, firstRegisterMask >> 32, AnyFrameIndex))); - } -} - -class OperationEvent: public Event { - public: - OperationEvent(Context* c, Operation op): - Event(c), op(op) - { } - - virtual const char* name() { - return "OperationEvent"; - } - - virtual void compile(Context* c) { - c->assembler->apply(op); - } - - Operation op; -}; - -void -appendOperation(Context* c, Operation op) -{ - append - (c, new(c->zone) OperationEvent(c, op)); -} - -class MemoryEvent: public Event { - public: - MemoryEvent(Context* c, Value* base, int displacement, Value* index, - unsigned scale, Value* result): - Event(c), base(base), displacement(displacement), index(index), - scale(scale), result(result) - { - addRead(c, this, base, generalRegisterMask(c)); - if (index) { - addRead(c, this, index, generalRegisterOrConstantMask(c)); - } - } - - virtual const char* name() { - return "MemoryEvent"; - } - - virtual void compile(Context* c) { - int indexRegister; - int displacement = this->displacement; - unsigned scale = this->scale; - if (index) { - ConstantSite* constant = findConstantSite(c, index); - - if (constant) { - indexRegister = NoRegister; - displacement += (constant->value->value() * scale); - scale = 1; - } else { - assert(c, index->source->type(c) == RegisterOperand); - indexRegister = static_cast(index->source)->number; - } - } else { - indexRegister = NoRegister; - } - assert(c, base->source->type(c) == RegisterOperand); - int baseRegister = static_cast(base->source)->number; - - popRead(c, this, base); - if (index) { - if (TargetBytesPerWord == 8 and indexRegister != NoRegister) { - apply(c, Move, 4, index->source, index->source, - 8, index->source, index->source); - } - - popRead(c, this, index); - } - - Site* site = memorySite - (c, baseRegister, displacement, indexRegister, scale); - - Site* low; - if (result->nextWord != result) { - Site* high = site->copyHigh(c); - low = site->copyLow(c); - - result->nextWord->target = high; - addSite(c, result->nextWord, high); - } else { - low = site; - } - - result->target = low; - addSite(c, result, low); - } - - Value* base; - int displacement; - Value* index; - unsigned scale; - Value* result; -}; - -void -appendMemory(Context* c, Value* base, int displacement, Value* index, - unsigned scale, Value* result) -{ - append(c, new(c->zone) - MemoryEvent(c, base, displacement, index, scale, result)); -} - -double -asFloat(unsigned size, int64_t v) -{ - if (size == 4) { - return bitsToFloat(v); - } else { - return bitsToDouble(v); - } -} - -bool -unordered(double a, double b) -{ - return not (a >= b or a < b); -} - -bool -shouldJump(Context* c, TernaryOperation type, unsigned size, int64_t b, - int64_t a) -{ - switch (type) { - case JumpIfEqual: - return a == b; - - case JumpIfNotEqual: - return a != b; - - case JumpIfLess: - return a < b; - - case JumpIfGreater: - return a > b; - - case JumpIfLessOrEqual: - return a <= b; - - case JumpIfGreaterOrEqual: - return a >= b; - - case JumpIfFloatEqual: - return asFloat(size, a) == asFloat(size, b); - - case JumpIfFloatNotEqual: - return asFloat(size, a) != asFloat(size, b); - - case JumpIfFloatLess: - return asFloat(size, a) < asFloat(size, b); - - case JumpIfFloatGreater: - return asFloat(size, a) > asFloat(size, b); - - case JumpIfFloatLessOrEqual: - return asFloat(size, a) <= asFloat(size, b); - - case JumpIfFloatGreaterOrEqual: - return asFloat(size, a) >= asFloat(size, b); - - case JumpIfFloatLessOrUnordered: - return asFloat(size, a) < asFloat(size, b) - or unordered(asFloat(size, a), asFloat(size, b)); - - case JumpIfFloatGreaterOrUnordered: - return asFloat(size, a) > asFloat(size, b) - or unordered(asFloat(size, a), asFloat(size, b)); - - case JumpIfFloatLessOrEqualOrUnordered: - return asFloat(size, a) <= asFloat(size, b) - or unordered(asFloat(size, a), asFloat(size, b)); - - case JumpIfFloatGreaterOrEqualOrUnordered: - return asFloat(size, a) >= asFloat(size, b) - or unordered(asFloat(size, a), asFloat(size, b)); - - default: - abort(c); - } -} - -TernaryOperation -thunkBranch(Context* c, TernaryOperation type) -{ - switch (type) { - case JumpIfFloatEqual: - return JumpIfEqual; - - case JumpIfFloatNotEqual: - return JumpIfNotEqual; - - case JumpIfFloatLess: - case JumpIfFloatLessOrUnordered: - return JumpIfLess; - - case JumpIfFloatGreater: - case JumpIfFloatGreaterOrUnordered: - return JumpIfGreater; - - case JumpIfFloatLessOrEqual: - case JumpIfFloatLessOrEqualOrUnordered: - return JumpIfLessOrEqual; - - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatGreaterOrEqualOrUnordered: - return JumpIfGreaterOrEqual; - - default: - abort(c); - } -} - -class BranchEvent: public Event { - public: - BranchEvent(Context* c, TernaryOperation type, unsigned size, - Value* first, Value* second, Value* address, - const SiteMask& firstLowMask, - const SiteMask& firstHighMask, - const SiteMask& secondLowMask, - const SiteMask& secondHighMask): - Event(c), type(type), size(size), first(first), second(second), - address(address) - { - addReads(c, this, first, size, firstLowMask, firstHighMask); - addReads(c, this, second, size, secondLowMask, secondHighMask); - - uint8_t typeMask; - uint64_t registerMask; - c->arch->planDestination(type, size, 0, 0, size, 0, 0, TargetBytesPerWord, - &typeMask, ®isterMask); - - addRead(c, this, address, SiteMask(typeMask, registerMask, AnyFrameIndex)); - } - - virtual const char* name() { - return "BranchEvent"; - } - - virtual void compile(Context* c) { - ConstantSite* firstConstant = findConstantSite(c, first); - ConstantSite* secondConstant = findConstantSite(c, second); - - if (not unreachable(this)) { - if (firstConstant - and secondConstant - and firstConstant->value->resolved() - and secondConstant->value->resolved()) - { - int64_t firstValue = firstConstant->value->value(); - int64_t secondValue = secondConstant->value->value(); - - if (size > TargetBytesPerWord) { - firstValue |= findConstantSite - (c, first->nextWord)->value->value() << 32; - secondValue |= findConstantSite - (c, second->nextWord)->value->value() << 32; - } - - if (shouldJump(c, type, size, firstValue, secondValue)) { - apply(c, Jump, TargetBytesPerWord, address->source, address->source); - } - } else { - freezeSource(c, size, first); - freezeSource(c, size, second); - freezeSource(c, TargetBytesPerWord, address); - - apply(c, type, size, first->source, first->nextWord->source, - size, second->source, second->nextWord->source, - TargetBytesPerWord, address->source, address->source); - - thawSource(c, TargetBytesPerWord, address); - thawSource(c, size, second); - thawSource(c, size, first); - } - } - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - } - - virtual bool isBranch() { return true; } - - TernaryOperation type; - unsigned size; - Value* first; - Value* second; - Value* address; -}; - -void -appendBranch(Context* c, TernaryOperation type, unsigned size, Value* first, - Value* second, Value* address) -{ - bool thunk; - uint8_t firstTypeMask; - uint64_t firstRegisterMask; - uint8_t secondTypeMask; - uint64_t secondRegisterMask; - - c->arch->planSource(type, size, &firstTypeMask, &firstRegisterMask, - size, &secondTypeMask, &secondRegisterMask, - TargetBytesPerWord, &thunk); - - if (thunk) { - Stack* oldStack = c->stack; - - bool threadParameter; - intptr_t handler = c->client->getThunk - (type, size, size, &threadParameter); - - assert(c, not threadParameter); - - local::push(c, ceiling(size, TargetBytesPerWord), second); - local::push(c, ceiling(size, TargetBytesPerWord), first); - - Stack* argumentStack = c->stack; - c->stack = oldStack; - - Value* result = value(c, ValueGeneral); - appendCall - (c, value - (c, ValueGeneral, constantSite(c, handler)), 0, 0, result, 4, - argumentStack, ceiling(size, TargetBytesPerWord) * 2, 0); - - appendBranch(c, thunkBranch(c, type), 4, value - (c, ValueGeneral, constantSite(c, static_cast(0))), - result, address); - } else { - append - (c, new(c->zone) - BranchEvent - (c, type, size, first, second, address, - SiteMask(firstTypeMask, firstRegisterMask, AnyFrameIndex), - SiteMask(firstTypeMask, firstRegisterMask >> 32, AnyFrameIndex), - SiteMask(secondTypeMask, secondRegisterMask, AnyFrameIndex), - SiteMask(secondTypeMask, secondRegisterMask >> 32, AnyFrameIndex))); - } -} - -class JumpEvent: public Event { - public: - JumpEvent(Context* c, UnaryOperation type, Value* address, bool exit, - bool cleanLocals): - Event(c), type(type), address(address), exit(exit), - cleanLocals(cleanLocals) - { - bool thunk; - uint8_t typeMask; - uint64_t registerMask; - c->arch->plan(type, TargetBytesPerWord, &typeMask, ®isterMask, &thunk); - - assert(c, not thunk); - - addRead(c, this, address, SiteMask(typeMask, registerMask, AnyFrameIndex)); - } - - virtual const char* name() { - return "JumpEvent"; - } - - virtual void compile(Context* c) { - if (not unreachable(this)) { - apply(c, type, TargetBytesPerWord, address->source, address->source); - } - - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - - if (cleanLocals) { - for (FrameIterator it(c, 0, c->locals); it.hasMore();) { - FrameIterator::Element e = it.next(c); - clean(c, e.value, 0); - } - } - } - - virtual bool isBranch() { return true; } - - virtual bool allExits() { - return exit or unreachable(this); - } - - UnaryOperation type; - Value* address; - bool exit; - bool cleanLocals; -}; - -void -appendJump(Context* c, UnaryOperation type, Value* address, bool exit = false, - bool cleanLocals = false) -{ - append(c, new(c->zone) JumpEvent(c, type, address, exit, cleanLocals)); -} - -class BoundsCheckEvent: public Event { - public: - BoundsCheckEvent(Context* c, Value* object, unsigned lengthOffset, - Value* index, intptr_t handler): - Event(c), object(object), lengthOffset(lengthOffset), index(index), - handler(handler) - { - addRead(c, this, object, generalRegisterMask(c)); - addRead(c, this, index, generalRegisterOrConstantMask(c)); - } - - virtual const char* name() { - return "BoundsCheckEvent"; - } - - virtual void compile(Context* c) { - Assembler* a = c->assembler; - - ConstantSite* constant = findConstantSite(c, index); - CodePromise* outOfBoundsPromise = 0; - - if (constant) { - if (constant->value->value() < 0) { - Assembler::Constant handlerConstant(resolved(c, handler)); - a->apply(Call, TargetBytesPerWord, ConstantOperand, &handlerConstant); - } - } else { - outOfBoundsPromise = codePromise(c, static_cast(0)); - - ConstantSite zero(resolved(c, 0)); - ConstantSite oob(outOfBoundsPromise); - apply(c, JumpIfLess, 4, &zero, &zero, 4, index->source, index->source, - TargetBytesPerWord, &oob, &oob); - } - - if (constant == 0 or constant->value->value() >= 0) { - assert(c, object->source->type(c) == RegisterOperand); - MemorySite length(static_cast(object->source)->number, - lengthOffset, NoRegister, 1); - length.acquired = true; - - CodePromise* nextPromise = codePromise(c, static_cast(0)); - - freezeSource(c, TargetBytesPerWord, index); - - ConstantSite next(nextPromise); - apply(c, JumpIfGreater, 4, index->source, index->source, 4, &length, - &length, TargetBytesPerWord, &next, &next); - - thawSource(c, TargetBytesPerWord, index); - - if (constant == 0) { - outOfBoundsPromise->offset = a->offset(); - } - - Assembler::Constant handlerConstant(resolved(c, handler)); - a->apply(Call, TargetBytesPerWord, ConstantOperand, &handlerConstant); - - nextPromise->offset = a->offset(); - } - - popRead(c, this, object); - popRead(c, this, index); - } - - Value* object; - unsigned lengthOffset; - Value* index; - intptr_t handler; -}; - -void -appendBoundsCheck(Context* c, Value* object, unsigned lengthOffset, - Value* index, intptr_t handler) -{ - append(c, new(c->zone) BoundsCheckEvent(c, object, lengthOffset, index, handler)); -} - -class FrameSiteEvent: public Event { - public: - FrameSiteEvent(Context* c, Value* value, int index): - Event(c), value(value), index(index) - { } - - virtual const char* name() { - return "FrameSiteEvent"; - } - - virtual void compile(Context* c) { - if (live(c, value)) { - addSite(c, value, frameSite(c, index)); - } - } - - Value* value; - int index; -}; - -void -appendFrameSite(Context* c, Value* value, int index) -{ - append(c, new(c->zone) FrameSiteEvent(c, value, index)); -} - -unsigned -frameFootprint(Context* c, Stack* s) -{ - return c->localFootprint + (s ? (s->index + 1) : 0); -} - -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); - - ForkState* forkState = link->forkState; - if (forkState) { - for (unsigned i = 0; i < forkState->readCount; ++i) { - 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); - if (not live(c, v)) { - clearSites(c, v); - } - } - } - - JunctionState* junctionState = link->junctionState; - if (junctionState) { - for (unsigned i = 0; i < junctionState->frameFootprint; ++i) { - StubReadPair* p = junctionState->reads + i; - - if (p->value and p->value->reads) { - assert(c, p->value->reads == p->read); - popRead(c, 0, p->value); - } - } - } -} - -class BuddyEvent: public Event { - public: - BuddyEvent(Context* c, Value* original, Value* buddy): - Event(c), original(original), buddy(buddy) - { - addRead(c, this, original, SiteMask(~0, ~0, AnyFrameIndex), buddy); - } - - virtual const char* name() { - return "BuddyEvent"; - } - - virtual void compile(Context* c) { - if (DebugBuddies) { - fprintf(stderr, "original %p buddy %p\n", original, buddy); - } - - assert(c, hasSite(c, original)); - - assert(c, original); - assert(c, buddy); - - addBuddy(original, buddy); - - popRead(c, this, original); - } - - Value* original; - Value* buddy; -}; - -void -appendBuddy(Context* c, Value* original, Value* buddy) -{ - append(c, new(c->zone) BuddyEvent(c, original, buddy)); -} - -class SaveLocalsEvent: public Event { - public: - SaveLocalsEvent(Context* c): - Event(c) - { - saveLocals(c, this); - } - - virtual const char* name() { - return "SaveLocalsEvent"; - } - - virtual void compile(Context* c) { - for (Read* r = reads; r; r = r->eventNext) { - popRead(c, this, r->value); - } - } -}; - -void -appendSaveLocals(Context* c) -{ - append(c, new(c->zone) SaveLocalsEvent(c)); -} - -class DummyEvent: public Event { - public: - DummyEvent(Context* c): - Event(c) - { } - - virtual const char* name() { - return "DummyEvent"; - } - - virtual void compile(Context*) { } -}; - -void -appendDummy(Context* c) -{ - Stack* stack = c->stack; - Local* locals = c->locals; - LogicalInstruction* i = c->logicalCode[c->logicalIp]; - - c->stack = i->stack; - c->locals = i->locals; - - append(c, new(c->zone) DummyEvent(c)); - - c->stack = stack; - c->locals = locals; -} - -void -append(Context* c, Event* e) -{ - LogicalInstruction* i = c->logicalCode[c->logicalIp]; - if (c->stack != i->stack or c->locals != i->locals) { - appendDummy(c); - } - - if (DebugAppend) { - fprintf(stderr, " -- append %s at %d with %d stack before\n", - e->name(), e->logicalInstruction->index, c->stack ? - c->stack->index + 1 : 0); - } - - if (c->lastEvent) { - c->lastEvent->next = e; - } else { - c->firstEvent = e; - } - c->lastEvent = e; - - Event* p = c->predecessor; - if (p) { - if (DebugAppend) { - fprintf(stderr, "%d precedes %d\n", p->logicalInstruction->index, - e->logicalInstruction->index); - } - - Link* link = local::link - (c, p, e->predecessors, e, p->successors, c->forkState); - e->predecessors = link; - p->successors = link; - } - c->forkState = 0; - - c->predecessor = e; - - if (e->logicalInstruction->firstEvent == 0) { - e->logicalInstruction->firstEvent = e; - } - e->logicalInstruction->lastEvent = e; -} - -Site* -readSource(Context* c, Read* r) -{ - Value* v = r->value; - - if (DebugReads) { - char buffer[1024]; sitesToString(c, v, buffer, 1024); - fprintf(stderr, "read source for %p from %s\n", v, buffer); - } - - if (not hasSite(c, v)) { - if (DebugReads) { - fprintf(stderr, "no sites found for %p\n", v); - } - return 0; - } - - Value* high = r->high(c); - if (high) { - return pickMatchOrMove(c, r, high->source, 0, true); - } else { - return pickSiteOrMove(c, r, true, true); - } -} - -void -propagateJunctionSites(Context* c, Event* e, Site** sites) -{ - for (Link* pl = e->predecessors; pl; pl = pl->nextPredecessor) { - Event* p = pl->predecessor; - if (p->junctionSites == 0) { - p->junctionSites = sites; - for (Link* sl = p->successors; sl; sl = sl->nextSuccessor) { - Event* s = sl->successor; - propagateJunctionSites(c, s, sites); - } - } - } -} - -void -propagateJunctionSites(Context* c, Event* e) -{ - for (Link* sl = e->successors; sl; sl = sl->nextSuccessor) { - Event* s = sl->successor; - if (s->predecessors->nextPredecessor) { - unsigned size = sizeof(Site*) * frameFootprint(c, e->stackAfter); - Site** junctionSites = static_cast - (c->zone->allocate(size)); - memset(junctionSites, 0, size); - - propagateJunctionSites(c, s, junctionSites); - break; - } - } -} - -class SiteRecord { - public: - Site* site; - Value* value; -}; - -void -init(SiteRecord* r, Site* s, Value* v) -{ - r->site = s; - r->value = v; -} - -class SiteRecordList { - public: - SiteRecordList(SiteRecord* records, unsigned capacity): - records(records), index(0), capacity(capacity) - { } - - SiteRecord* records; - unsigned index; - unsigned capacity; -}; - -void -freeze(Context* c, SiteRecordList* frozen, Site* s, Value* v) -{ - assert(c, frozen->index < frozen->capacity); - - s->freeze(c, v); - init(new (frozen->records + (frozen->index ++)) SiteRecord, s, v); -} - -void -thaw(Context* c, SiteRecordList* frozen) -{ - while (frozen->index) { - SiteRecord* sr = frozen->records + (-- frozen->index); - sr->site->thaw(c, sr->value); - } -} - -bool -resolveOriginalSites(Context* c, Event* e, SiteRecordList* frozen, - Site** sites) -{ - bool complete = true; - for (FrameIterator it(c, e->stackAfter, e->localsAfter, true); - it.hasMore();) - { - FrameIterator::Element el = it.next(c); - Value* v = el.value; - Read* r = v ? live(c, v) : 0; - Site* s = sites[el.localIndex]; - - if (r) { - if (s) { - if (DebugControl) { - char buffer[256]; - s->toString(c, buffer, 256); - fprintf(stderr, "resolve original %s for %p local %d frame %d\n", - buffer, v, el.localIndex, frameIndex(c, &el)); - } - - Site* target = pickSiteOrMove - (c, v, s->mask(c), true, true, ResolveRegisterReserveCount); - - freeze(c, frozen, target, v); - } else { - complete = false; - } - } else if (s) { - if (DebugControl) { - char buffer[256]; - s->toString(c, buffer, 256); - fprintf(stderr, "freeze original %s for %p local %d frame %d\n", - buffer, v, el.localIndex, frameIndex(c, &el)); - } - - Value dummy(0, 0, ValueGeneral); - addSite(c, &dummy, s); - removeSite(c, &dummy, s); - freeze(c, frozen, s, 0); - } - } - - return complete; -} - -bool -resolveSourceSites(Context* c, Event* e, SiteRecordList* frozen, Site** sites) -{ - bool complete = true; - for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { - FrameIterator::Element el = it.next(c); - Value* v = el.value; - Read* r = live(c, v); - - if (r and sites[el.localIndex] == 0) { - SiteMask mask((1 << RegisterOperand) | (1 << MemoryOperand), - c->arch->generalRegisterMask(), AnyFrameIndex); - - Site* s = pickSourceSite - (c, r, 0, 0, &mask, true, false, true, acceptForResolve); - - if (s) { - if (DebugControl) { - char buffer[256]; s->toString(c, buffer, 256); - fprintf(stderr, "resolve source %s from %p local %d frame %d\n", - buffer, v, el.localIndex, frameIndex(c, &el)); - } - - freeze(c, frozen, s, v); - - sites[el.localIndex] = s->copy(c); - } else { - complete = false; - } - } - } - - return complete; -} - -void -resolveTargetSites(Context* c, Event* e, SiteRecordList* frozen, Site** sites) -{ - for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { - FrameIterator::Element el = it.next(c); - Value* v = el.value; - Read* r = live(c, v); - - if (r and sites[el.localIndex] == 0) { - SiteMask mask((1 << RegisterOperand) | (1 << MemoryOperand), - c->arch->generalRegisterMask(), AnyFrameIndex); - - Site* s = pickSourceSite - (c, r, 0, 0, &mask, false, true, true, acceptForResolve); - - if (s == 0) { - s = maybeMove(c, v, mask, false, true, ResolveRegisterReserveCount); - } - - freeze(c, frozen, s, v); - - sites[el.localIndex] = s->copy(c); - - if (DebugControl) { - char buffer[256]; sites[el.localIndex]->toString(c, buffer, 256); - fprintf(stderr, "resolve target %s for %p local %d frame %d\n", - buffer, el.value, el.localIndex, frameIndex(c, &el)); - } - } - } -} - -void -resolveJunctionSites(Context* c, Event* e, SiteRecordList* frozen) -{ - bool complete; - if (e->junctionSites) { - complete = resolveOriginalSites(c, e, frozen, e->junctionSites); - } else { - propagateJunctionSites(c, e); - complete = false; - } - - if (e->junctionSites) { - if (not complete) { - complete = resolveSourceSites(c, e, frozen, e->junctionSites); - if (not complete) { - resolveTargetSites(c, e, frozen, e->junctionSites); - } - } - - if (DebugControl) { - fprintf(stderr, "resolved junction sites %p at %d\n", - e->junctionSites, e->logicalInstruction->index); - } - } -} - -void -resolveBranchSites(Context* c, Event* e, SiteRecordList* frozen) -{ - if (e->successors->nextSuccessor and e->junctionSites == 0) { - unsigned footprint = frameFootprint(c, e->stackAfter); - RUNTIME_ARRAY(Site*, branchSites, footprint); - memset(RUNTIME_ARRAY_BODY(branchSites), 0, sizeof(Site*) * footprint); - - if (not resolveSourceSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites))) - { - resolveTargetSites(c, e, frozen, RUNTIME_ARRAY_BODY(branchSites)); - } - } -} - -void -captureBranchSnapshots(Context* c, Event* e) -{ - if (e->successors->nextSuccessor) { - for (FrameIterator it(c, e->stackAfter, e->localsAfter); it.hasMore();) { - FrameIterator::Element el = it.next(c); - e->snapshots = makeSnapshots(c, el.value, e->snapshots); - } - - for (Cell* sv = e->successors->forkState->saved; sv; sv = sv->next) { - e->snapshots = makeSnapshots - (c, static_cast(sv->value), e->snapshots); - } - - if (DebugControl) { - fprintf(stderr, "captured snapshots %p at %d\n", - e->snapshots, e->logicalInstruction->index); - } - } -} - -void -populateSiteTables(Context* c, Event* e, SiteRecordList* frozen) -{ - resolveJunctionSites(c, e, frozen); - - resolveBranchSites(c, e, frozen); -} - -void -setSites(Context* c, Value* v, Site* s) -{ - assert(c, live(c, v)); - - for (; s; s = s->next) { - addSite(c, v, s->copy(c)); - } - - if (DebugControl) { - char buffer[256]; sitesToString(c, v->sites, buffer, 256); - fprintf(stderr, "set sites %s for %p\n", buffer, v); - } -} - -void -resetFrame(Context* c, Event* e) -{ - for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { - FrameIterator::Element el = it.next(c); - clearSites(c, el.value); - } - - while (c->acquiredResources) { - clearSites(c, c->acquiredResources->value); - } -} - -void -setSites(Context* c, Event* e, Site** sites) -{ - resetFrame(c, e); - - for (FrameIterator it(c, e->stackBefore, e->localsBefore); it.hasMore();) { - FrameIterator::Element el = it.next(c); - if (sites[el.localIndex]) { - if (live(c, el.value)) { - setSites(c, el.value, sites[el.localIndex]); - } else if (DebugControl) { - char buffer[256]; sitesToString(c, sites[el.localIndex], buffer, 256); - fprintf(stderr, "skip sites %s for %p local %d frame %d\n", - buffer, el.value, el.localIndex, frameIndex(c, &el)); - } - } else if (DebugControl) { - fprintf(stderr, "no sites for %p local %d frame %d\n", - el.value, el.localIndex, frameIndex(c, &el)); - } - } -} - -void -removeBuddies(Context* c) -{ - for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { - FrameIterator::Element el = it.next(c); - removeBuddy(c, el.value); - } -} - -void -restore(Context* c, Event* e, Snapshot* snapshots) -{ - for (Snapshot* s = snapshots; s; s = s->next) { - Value* v = s->value; - Value* next = v->buddy; - if (v != next) { - v->buddy = v; - Value* p = next; - while (p->buddy != v) p = p->buddy; - p->buddy = next; - } - } - - for (Snapshot* s = snapshots; s; s = s->next) { - assert(c, s->buddy); - - s->value->buddy = s->buddy; - } - - resetFrame(c, e); - - for (Snapshot* s = snapshots; s; s = s->next) { - if (live(c, s->value)) { - if (live(c, s->value) and s->sites and s->value->sites == 0) { - setSites(c, s->value, s->sites); - } - } - - // 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(c, s->value)); - } -} - -void -populateSources(Context* c, Event* e) -{ - RUNTIME_ARRAY(SiteRecord, frozenRecords, e->readCount); - SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), e->readCount); - - for (Read* r = e->reads; r; r = r->eventNext) { - r->value->source = readSource(c, r); - if (r->value->source) { - if (DebugReads) { - char buffer[256]; r->value->source->toString(c, buffer, 256); - fprintf(stderr, "freeze source %s for %p\n", - buffer, r->value); - } - - freeze(c, &frozen, r->value->source, r->value); - } - } - - thaw(c, &frozen); -} - -void -setStubRead(Context* c, StubReadPair* p, Value* v) -{ - if (v) { - StubRead* r = stubRead(c); - if (DebugReads) { - fprintf(stderr, "add stub read %p to %p\n", r, v); - } - addRead(c, 0, v, r); - - p->value = v; - p->read = r; - } -} - -void -populateJunctionReads(Context* c, Link* link) -{ - JunctionState* state = new - (c->zone->allocate - (sizeof(JunctionState) - + (sizeof(StubReadPair) * frameFootprint(c, c->stack)))) - JunctionState(frameFootprint(c, c->stack)); - - memset(state->reads, 0, sizeof(StubReadPair) * frameFootprint(c, c->stack)); - - link->junctionState = state; - - for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { - FrameIterator::Element e = it.next(c); - setStubRead(c, state->reads + e.localIndex, e.value); - } -} - -void -updateJunctionReads(Context* c, JunctionState* state) -{ - for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { - FrameIterator::Element e = it.next(c); - StubReadPair* p = state->reads + e.localIndex; - if (p->value and p->read->read == 0) { - Read* r = live(c, e.value); - if (r) { - if (DebugReads) { - fprintf(stderr, "stub read %p for %p valid: %p\n", - p->read, p->value, r); - } - p->read->read = r; - } - } - } - - for (unsigned i = 0; i < frameFootprint(c, c->stack); ++i) { - StubReadPair* p = state->reads + i; - if (p->value and p->read->read == 0) { - if (DebugReads) { - fprintf(stderr, "stub read %p for %p invalid\n", p->read, p->value); - } - p->read->valid_ = false; - } - } -} - -LogicalInstruction* -next(Context* c, LogicalInstruction* i) -{ - for (unsigned n = i->index + 1; n < c->logicalCodeLength; ++n) { - i = c->logicalCode[n]; - if (i) return i; - } - return 0; -} - -class Block { - public: - Block(Event* head): - head(head), nextBlock(0), nextInstruction(0), assemblerBlock(0), start(0) - { } - - Event* head; - Block* nextBlock; - LogicalInstruction* nextInstruction; - Assembler::Block* assemblerBlock; - unsigned start; -}; - -Block* -block(Context* c, Event* head) -{ - return new(c->zone) Block(head); -} - -void -compile(Context* c, uintptr_t stackOverflowHandler, unsigned stackLimitOffset) -{ - if (c->logicalCode[c->logicalIp]->lastEvent == 0) { - appendDummy(c); - } - - Assembler* a = c->assembler; - - Block* firstBlock = block(c, c->firstEvent); - Block* block = firstBlock; - - if (stackOverflowHandler) { - a->checkStackOverflow(stackOverflowHandler, stackLimitOffset); - } - - a->allocateFrame(c->alignedFrameSize); - - for (Event* e = c->firstEvent; e; e = e->next) { - if (DebugCompile) { - fprintf(stderr, - " -- compile %s at %d with %d preds %d succs %d stack\n", - e->name(), e->logicalInstruction->index, - countPredecessors(e->predecessors), - countSuccessors(e->successors), - e->stackBefore ? e->stackBefore->index + 1 : 0); - } - - e->block = block; - - c->stack = e->stackBefore; - c->locals = e->localsBefore; - - if (e->logicalInstruction->machineOffset == 0) { - e->logicalInstruction->machineOffset = a->offset(); - } - - if (e->predecessors) { - visit(c, lastPredecessor(e->predecessors)); - - Event* first = e->predecessors->predecessor; - if (e->predecessors->nextPredecessor) { - for (Link* pl = e->predecessors; - pl->nextPredecessor; - pl = pl->nextPredecessor) - { - updateJunctionReads(c, pl->junctionState); - } - - if (DebugControl) { - fprintf(stderr, "set sites to junction sites %p at %d\n", - first->junctionSites, first->logicalInstruction->index); - } - - setSites(c, e, first->junctionSites); - removeBuddies(c); - } else if (first->successors->nextSuccessor) { - if (DebugControl) { - fprintf(stderr, "restore snapshots %p at %d\n", - first->snapshots, first->logicalInstruction->index); - } - - restore(c, e, first->snapshots); - } - } - - unsigned footprint = frameFootprint(c, e->stackAfter); - RUNTIME_ARRAY(SiteRecord, frozenRecords, footprint); - SiteRecordList frozen(RUNTIME_ARRAY_BODY(frozenRecords), footprint); - - bool branch = e->isBranch(); - if (branch and e->successors) { - populateSiteTables(c, e, &frozen); - } - - populateSources(c, e); - - if (branch and e->successors) { - captureBranchSnapshots(c, e); - } - - thaw(c, &frozen); - - e->compile(c); - - if ((not branch) and e->successors) { - populateSiteTables(c, e, &frozen); - captureBranchSnapshots(c, e); - thaw(c, &frozen); - } - - if (e->visitLinks) { - for (Cell* cell = reverseDestroy(e->visitLinks); cell; cell = cell->next) - { - visit(c, static_cast(cell->value)); - } - e->visitLinks = 0; - } - - for (CodePromise* p = e->promises; p; p = p->next) { - p->offset = a->offset(); - } - - a->endEvent(); - - LogicalInstruction* nextInstruction = next(c, e->logicalInstruction); - if (e->next == 0 - or (e->next->logicalInstruction != e->logicalInstruction - and (e->next->logicalInstruction != nextInstruction - or e != e->logicalInstruction->lastEvent))) - { - Block* b = e->logicalInstruction->firstEvent->block; - - while (b->nextBlock) { - b = b->nextBlock; - } - - if (b != block) { - b->nextBlock = block; - } - - block->nextInstruction = nextInstruction; - block->assemblerBlock = a->endBlock(e->next != 0); - - if (e->next) { - block = local::block(c, e->next); - } - } - } - - c->firstBlock = firstBlock; -} - -void -restore(Context* c, ForkState* state) -{ - for (unsigned i = 0; i < state->readCount; ++i) { - ForkElement* p = state->elements + i; - p->value->lastRead = p->read; - p->read->allocateTarget(c); - } -} - -void -addForkElement(Context* c, Value* v, ForkState* state, unsigned index) -{ - MultiRead* r = multiRead(c); - if (DebugReads) { - fprintf(stderr, "add multi read %p to %p\n", r, v); - } - addRead(c, 0, v, r); - - ForkElement* p = state->elements + index; - p->value = v; - p->read = r; -} - -ForkState* -saveState(Context* c) -{ - if (c->logicalCode[c->logicalIp]->lastEvent == 0) { - appendDummy(c); - } - - unsigned elementCount = frameFootprint(c, c->stack) + count(c->saved); - - ForkState* state = new - (c->zone->allocate - (sizeof(ForkState) + (sizeof(ForkElement) * elementCount))) - ForkState(c->stack, c->locals, c->saved, c->predecessor, c->logicalIp); - - if (c->predecessor) { - c->forkState = state; - - unsigned count = 0; - - for (FrameIterator it(c, c->stack, c->locals); it.hasMore();) { - FrameIterator::Element e = it.next(c); - addForkElement(c, e.value, state, count++); - } - - for (Cell* sv = c->saved; sv; sv = sv->next) { - addForkElement(c, static_cast(sv->value), state, count++); - } - - state->readCount = count; - } - - c->saved = 0; - - return state; -} - -void -restoreState(Context* c, ForkState* s) -{ - if (c->logicalCode[c->logicalIp]->lastEvent == 0) { - appendDummy(c); - } - - c->stack = s->stack; - c->locals = s->locals; - c->predecessor = s->predecessor; - c->logicalIp = s->logicalIp; - - if (c->predecessor) { - c->forkState = s; - restore(c, s); - } -} - -Value* -maybeBuddy(Context* c, Value* v) -{ - if (v->home >= 0) { - Value* n = value(c, v->type); - appendBuddy(c, v, n); - return n; - } else { - return v; - } -} - -void -linkLocals(Context* c, Local* oldLocals, Local* newLocals) -{ - for (int i = 0; i < static_cast(c->localFootprint); ++i) { - Local* local = oldLocals + i; - if (local->value) { - int highOffset = c->arch->bigEndian() ? 1 : -1; - - if (i + highOffset >= 0 - and i + highOffset < static_cast(c->localFootprint) - and local->value->nextWord == local[highOffset].value) - { - Value* v = newLocals[i].value; - Value* next = newLocals[i + highOffset].value; - v->nextWord = next; - next->nextWord = v; - next->wordIndex = 1; - } - } - } -} - -class Client: public Assembler::Client { - public: - Client(Context* c): c(c) { } - - virtual int acquireTemporary(uint32_t mask) { - unsigned cost; - int r = pickRegisterTarget(c, 0, mask, &cost); - expect(c, cost < Target::Impossible); - save(r); - increment(c, c->registerResources + r); - return r; - } - - virtual void releaseTemporary(int r) { - decrement(c, c->registerResources + r); - } - - virtual void save(int r) { - RegisterResource* reg = c->registerResources + r; - - assert(c, reg->referenceCount == 0); - assert(c, reg->freezeCount == 0); - assert(c, not reg->reserved); - - if (reg->value) { - steal(c, reg, 0); - } - } - - Context* c; -}; - -class MyCompiler: public Compiler { - public: - MyCompiler(System* s, Assembler* assembler, Zone* zone, - Compiler::Client* compilerClient): - c(s, assembler, zone, compilerClient), client(&c) - { - assembler->setClient(&client); - } - - virtual State* saveState() { - State* s = local::saveState(&c); - restoreState(s); - return s; - } - - virtual void restoreState(State* state) { - local::restoreState(&c, static_cast(state)); - } - - virtual Subroutine* startSubroutine() { - return c.subroutine = new(c.zone) MySubroutine; - } - - virtual void returnFromSubroutine(Subroutine* subroutine, Operand* address) { - appendSaveLocals(&c); - appendJump(&c, Jump, static_cast(address), false, true); - static_cast(subroutine)->forkState = local::saveState(&c); - } - - virtual void linkSubroutine(Subroutine* subroutine) { - Local* oldLocals = c.locals; - restoreState(static_cast(subroutine)->forkState); - linkLocals(&c, oldLocals, c.locals); - } - - virtual void init(unsigned logicalCodeLength, unsigned parameterFootprint, - unsigned localFootprint, unsigned alignedFrameSize) - { - c.logicalCodeLength = logicalCodeLength; - c.parameterFootprint = parameterFootprint; - c.localFootprint = localFootprint; - c.alignedFrameSize = alignedFrameSize; - - unsigned frameResourceCount = totalFrameSize(&c); - - c.frameResources = static_cast - (c.zone->allocate(sizeof(FrameResource) * frameResourceCount)); - - for (unsigned i = 0; i < frameResourceCount; ++i) { - new (c.frameResources + i) FrameResource; - } - - unsigned base = frameBase(&c); - c.frameResources[base + c.arch->returnAddressOffset()].reserved = true; - c.frameResources[base + c.arch->framePointerOffset()].reserved - = UseFramePointer; - - // leave room for logical instruction -1 - unsigned codeSize = sizeof(LogicalInstruction*) * (logicalCodeLength + 1); - c.logicalCode = static_cast - (c.zone->allocate(codeSize)); - memset(c.logicalCode, 0, codeSize); - c.logicalCode++; - - c.locals = static_cast - (c.zone->allocate(sizeof(Local) * localFootprint)); - - memset(c.locals, 0, sizeof(Local) * localFootprint); - - c.logicalCode[-1] = new - (c.zone->allocate(sizeof(LogicalInstruction))) - LogicalInstruction(-1, c.stack, c.locals); - } - - virtual void visitLogicalIp(unsigned logicalIp) { - assert(&c, logicalIp < c.logicalCodeLength); - - if (c.logicalCode[c.logicalIp]->lastEvent == 0) { - appendDummy(&c); - } - - Event* e = c.logicalCode[logicalIp]->firstEvent; - - Event* p = c.predecessor; - if (p) { - if (DebugAppend) { - fprintf(stderr, "visit %d pred %d\n", logicalIp, - p->logicalInstruction->index); - } - - p->stackAfter = c.stack; - p->localsAfter = c.locals; - - Link* link = local::link - (&c, p, e->predecessors, e, p->successors, c.forkState); - e->predecessors = link; - p->successors = link; - c.lastEvent->visitLinks = cons(&c, link, c.lastEvent->visitLinks); - - if (DebugAppend) { - fprintf(stderr, "populate junction reads for %d to %d\n", - p->logicalInstruction->index, logicalIp); - } - - populateJunctionReads(&c, link); - } - - if (c.subroutine) { - c.subroutine->forkState - = c.logicalCode[logicalIp]->subroutine->forkState; - c.subroutine = 0; - } - - c.forkState = 0; - } - - virtual void startLogicalIp(unsigned logicalIp) { - assert(&c, logicalIp < c.logicalCodeLength); - assert(&c, c.logicalCode[logicalIp] == 0); - - if (c.logicalCode[c.logicalIp]->lastEvent == 0) { - appendDummy(&c); - } - - Event* p = c.predecessor; - if (p) { - p->stackAfter = c.stack; - p->localsAfter = c.locals; - } - - c.logicalCode[logicalIp] = new - (c.zone->allocate(sizeof(LogicalInstruction))) - LogicalInstruction(logicalIp, c.stack, c.locals); - - 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, IntegerType); - } - } - } - } - - virtual Promise* machineIp(unsigned logicalIp) { - return new(c.zone) IpPromise(&c, logicalIp); - } - - virtual Promise* poolAppend(intptr_t value) { - return poolAppendPromise(resolved(&c, value)); - } - - virtual Promise* poolAppendPromise(Promise* value) { - Promise* p = new(c.zone) PoolPromise(&c, c.constantCount); - - ConstantPoolNode* constant = new (c.zone) ConstantPoolNode(value); - - if (c.firstConstant) { - c.lastConstant->next = constant; - } else { - c.firstConstant = constant; - } - c.lastConstant = constant; - ++ c.constantCount; - - return p; - } - - virtual Operand* constant(int64_t value, OperandType type) { - return promiseConstant(resolved(&c, value), type); - } - - virtual Operand* promiseConstant(Promise* value, OperandType type) { - return local::value - (&c, valueType(&c, type), local::constantSite(&c, value)); - } - - virtual Operand* address(Promise* address) { - return value(&c, ValueGeneral, local::addressSite(&c, address)); - } - - virtual Operand* memory(Operand* base, - OperandType type, - int displacement = 0, - Operand* index = 0, - unsigned scale = 1) - { - Value* result = value(&c, valueType(&c, type)); - - appendMemory(&c, static_cast(base), displacement, - static_cast(index), scale, result); - - return result; - } - - virtual Operand* register_(int number) { - return local::register_(&c, number); - } - - Promise* machineIp() { - return codePromise(&c, c.logicalCode[c.logicalIp]->lastEvent); - } - - virtual void push(unsigned footprint UNUSED) { - assert(&c, footprint == 1); - - Value* v = value(&c, ValueGeneral); - Stack* s = local::stack(&c, v, c.stack); - - v->home = frameIndex(&c, s->index + c.localFootprint); - c.stack = s; - } - - virtual void push(unsigned footprint, Operand* value) { - local::push(&c, footprint, static_cast(value)); - } - - virtual void save(unsigned footprint, Operand* value) { - c.saved = cons(&c, static_cast(value), c.saved); - if (TargetBytesPerWord == 4 and footprint > 1) { - assert(&c, footprint == 2); - assert(&c, static_cast(value)->nextWord); - - save(1, static_cast(value)->nextWord); - } - } - - virtual Operand* pop(unsigned footprint) { - return local::pop(&c, footprint); - } - - virtual void pushed() { - Value* v = value(&c, ValueGeneral); - appendFrameSite - (&c, v, frameIndex - (&c, (c.stack ? c.stack->index : 0) + c.localFootprint)); - - Stack* s = local::stack(&c, v, c.stack); - v->home = frameIndex(&c, s->index + c.localFootprint); - c.stack = s; - } - - virtual void popped(unsigned footprint) { - for (; footprint; -- footprint) { - assert(&c, c.stack->value == 0 or c.stack->value->home >= 0); - - if (DebugFrame) { - fprintf(stderr, "popped %p\n", c.stack->value); - } - - c.stack = c.stack->next; - } - } - - virtual unsigned topOfStack() { - return c.stack->index; - } - - virtual Operand* peek(unsigned footprint, unsigned index) { - Stack* s = c.stack; - for (unsigned i = index; i > 0; --i) { - s = s->next; - } - - if (footprint > 1) { - assert(&c, footprint == 2); - - bool bigEndian = c.arch->bigEndian(); - -#ifndef NDEBUG - Stack* low; - Stack* high; - if (bigEndian) { - high = s; - low = s->next; - } else { - low = s; - high = s->next; - } - - assert(&c, (TargetBytesPerWord == 8 - and low->value->nextWord == low->value and high->value == 0) - or (TargetBytesPerWord == 4 - and low->value->nextWord == high->value)); -#endif // not NDEBUG - - if (bigEndian) { - s = s->next; - } - } - - return s->value; - } - - virtual Operand* call(Operand* address, - unsigned flags, - TraceHandler* traceHandler, - unsigned resultSize, - OperandType resultType, - unsigned argumentCount, - ...) - { - va_list a; va_start(a, argumentCount); - - bool bigEndian = c.arch->bigEndian(); - - unsigned footprint = 0; - unsigned size = TargetBytesPerWord; - RUNTIME_ARRAY(Value*, arguments, argumentCount); - int index = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - Value* o = va_arg(a, Value*); - if (o) { - if (bigEndian and size > TargetBytesPerWord) { - RUNTIME_ARRAY_BODY(arguments)[index++] = o->nextWord; - } - RUNTIME_ARRAY_BODY(arguments)[index] = o; - if ((not bigEndian) and size > TargetBytesPerWord) { - RUNTIME_ARRAY_BODY(arguments)[++index] = o->nextWord; - } - size = TargetBytesPerWord; - ++ index; - } else { - size = 8; - } - ++ footprint; - } - - va_end(a); - - Stack* argumentStack = c.stack; - for (int i = index - 1; i >= 0; --i) { - argumentStack = local::stack - (&c, RUNTIME_ARRAY_BODY(arguments)[i], argumentStack); - } - - Value* result = value(&c, valueType(&c, resultType)); - appendCall(&c, static_cast(address), flags, traceHandler, result, - resultSize, argumentStack, index, 0); - - return result; - } - - virtual Operand* stackCall(Operand* address, - unsigned flags, - TraceHandler* traceHandler, - unsigned resultSize, - OperandType resultType, - unsigned argumentFootprint) - { - Value* result = value(&c, valueType(&c, resultType)); - appendCall(&c, static_cast(address), flags, traceHandler, result, - resultSize, c.stack, 0, argumentFootprint); - return result; - } - - virtual void return_(unsigned size, Operand* value) { - appendReturn(&c, size, static_cast(value)); - } - - virtual void initLocal(unsigned footprint, unsigned index, OperandType type) - { - assert(&c, index + footprint <= c.localFootprint); - - Value* v = value(&c, valueType(&c, type)); - - if (footprint > 1) { - assert(&c, footprint == 2); - - unsigned highIndex; - unsigned lowIndex; - if (c.arch->bigEndian()) { - highIndex = index + 1; - lowIndex = index; - } else { - lowIndex = index + 1; - highIndex = index; - } - - if (TargetBytesPerWord == 4) { - initLocal(1, highIndex, type); - Value* next = c.locals[highIndex].value; - v->nextWord = next; - next->nextWord = v; - next->wordIndex = 1; - } - - index = lowIndex; - } - - if (DebugFrame) { - fprintf(stderr, "init local %p at %d (%d)\n", - v, index, frameIndex(&c, index)); - } - - appendFrameSite(&c, v, frameIndex(&c, index)); - - Local* local = c.locals + index; - local->value = v; - v->home = frameIndex(&c, index); - } - - virtual void initLocalsFromLogicalIp(unsigned logicalIp) { - assert(&c, logicalIp < c.logicalCodeLength); - - unsigned footprint = sizeof(Local) * c.localFootprint; - Local* newLocals = static_cast(c.zone->allocate(footprint)); - memset(newLocals, 0, footprint); - c.locals = newLocals; - - Event* e = c.logicalCode[logicalIp]->firstEvent; - for (int i = 0; i < static_cast(c.localFootprint); ++i) { - Local* local = e->localsBefore + i; - if (local->value) { - initLocal - (1, i, local->value->type == ValueGeneral ? IntegerType : FloatType); - } - } - - linkLocals(&c, e->localsBefore, newLocals); - } - - virtual void storeLocal(unsigned footprint, Operand* src, unsigned index) { - local::storeLocal(&c, footprint, static_cast(src), index, true); - } - - virtual Operand* loadLocal(unsigned footprint, unsigned index) { - return local::loadLocal(&c, footprint, index); - } - - virtual void saveLocals() { - appendSaveLocals(&c); - } - - virtual void checkBounds(Operand* object, unsigned lengthOffset, - Operand* index, intptr_t handler) - { - appendBoundsCheck(&c, static_cast(object), lengthOffset, - static_cast(index), handler); - } - - virtual void store(unsigned srcSize, Operand* src, unsigned dstSize, - Operand* dst) - { - appendMove(&c, Move, srcSize, srcSize, static_cast(src), - dstSize, static_cast(dst)); - } - - virtual Operand* load(unsigned srcSize, unsigned srcSelectSize, Operand* src, - unsigned dstSize) - { - assert(&c, dstSize >= TargetBytesPerWord); - - Value* dst = value(&c, static_cast(src)->type); - appendMove(&c, Move, srcSize, srcSelectSize, static_cast(src), - dstSize, dst); - return dst; - } - - virtual Operand* loadz(unsigned srcSize, unsigned srcSelectSize, - Operand* src, unsigned dstSize) - { - assert(&c, dstSize >= TargetBytesPerWord); - - Value* dst = value(&c, static_cast(src)->type); - appendMove(&c, MoveZ, srcSize, srcSelectSize, static_cast(src), - dstSize, dst); - return dst; - } - - virtual void jumpIfEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfNotEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfNotEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfLess(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfLess, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfGreater(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfGreater, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfLessOrEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfLessOrEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfGreaterOrEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueGeneral - and static_cast(b)->type == ValueGeneral); - - appendBranch(&c, JumpIfGreaterOrEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatNotEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatNotEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatLess(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatLess, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatGreater(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatGreater, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatLessOrEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatLessOrEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatGreaterOrEqual(unsigned size, Operand* a, Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatGreaterOrEqual, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatLessOrUnordered(unsigned size, Operand* a, - Operand* b, Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatLessOrUnordered, size, static_cast(a), - static_cast(b), static_cast(address)); - } - - virtual void jumpIfFloatGreaterOrUnordered(unsigned size, Operand* a, - Operand* b, Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatGreaterOrUnordered, size, - static_cast(a), static_cast(b), - static_cast(address)); - } - - virtual void jumpIfFloatLessOrEqualOrUnordered(unsigned size, Operand* a, - Operand* b, Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatLessOrEqualOrUnordered, size, - static_cast(a), static_cast(b), - static_cast(address)); - } - - virtual void jumpIfFloatGreaterOrEqualOrUnordered(unsigned size, Operand* a, - Operand* b, - Operand* address) - { - assert(&c, static_cast(a)->type == ValueFloat - and static_cast(b)->type == ValueFloat); - - appendBranch(&c, JumpIfFloatGreaterOrEqualOrUnordered, size, - static_cast(a), static_cast(b), - static_cast(address)); - } - - virtual void jmp(Operand* address) { - appendJump(&c, Jump, static_cast(address)); - } - - virtual void exit(Operand* address) { - appendJump(&c, Jump, static_cast(address), true); - } - - virtual Operand* add(unsigned size, Operand* a, Operand* b) { - 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) { - 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) { - 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) { - 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) { - 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) { - 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); - return result; - } - - virtual Operand* fsub(unsigned size, Operand* a, Operand* b) { - 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); - return result; - } - - virtual Operand* fmul(unsigned size, Operand* a, Operand* b) { - 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); - return result; - } - - virtual Operand* fdiv(unsigned size, Operand* a, Operand* b) { - 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) { - 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) { - assert(&c, static_cast(a)->type == ValueGeneral); - Value* result = value(&c, ValueGeneral); - appendCombine(&c, ShiftLeft, TargetBytesPerWord, static_cast(a), - size, static_cast(b), size, result); - return result; - } - - virtual Operand* shr(unsigned size, Operand* a, Operand* b) { - assert(&c, static_cast(a)->type == ValueGeneral); - Value* result = value(&c, ValueGeneral); - appendCombine(&c, ShiftRight, TargetBytesPerWord, static_cast(a), - size, static_cast(b), size, result); - return result; - } - - virtual Operand* ushr(unsigned size, Operand* a, Operand* b) { - assert(&c, static_cast(a)->type == ValueGeneral); - Value* result = value(&c, ValueGeneral); - appendCombine - (&c, UnsignedShiftRight, TargetBytesPerWord, static_cast(a), - size, static_cast(b), size, result); - return result; - } - - virtual Operand* and_(unsigned size, Operand* a, Operand* b) { - 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) { - 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) { - 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) { - 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) { - 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* abs(unsigned size, Operand* a) { - assert(&c, static_cast(a)->type == ValueGeneral); - Value* result = value(&c, ValueGeneral); - appendTranslate(&c, Absolute, size, static_cast(a), size, result); - return result; - } - - virtual Operand* fabs(unsigned size, Operand* a) { - assert(&c, static_cast(a)->type == ValueFloat); - Value* result = value(&c, ValueFloat); - appendTranslate - (&c, FloatAbsolute, size, static_cast(a), size, result); - return result; - } - - virtual Operand* fsqrt(unsigned size, Operand* a) { - assert(&c, static_cast(a)->type == ValueFloat); - Value* result = value(&c, ValueFloat); - appendTranslate - (&c, FloatSquareRoot, size, static_cast(a), size, result); - return result; - } - - virtual Operand* f2f(unsigned aSize, unsigned resSize, Operand* a) { - 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) { - 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) { - assert(&c, static_cast(a)->type == ValueGeneral); - Value* result = value(&c, ValueFloat); - appendTranslate - (&c, Int2Float, aSize, static_cast(a), resSize, result); - return result; - } - - virtual void trap() { - appendOperation(&c, Trap); - } - - virtual void loadBarrier() { - appendOperation(&c, LoadBarrier); - } - - virtual void storeStoreBarrier() { - appendOperation(&c, StoreStoreBarrier); - } - - virtual void storeLoadBarrier() { - appendOperation(&c, StoreLoadBarrier); - } - - virtual void compile(uintptr_t stackOverflowHandler, - unsigned stackLimitOffset) - { - local::compile(&c, stackOverflowHandler, stackLimitOffset); - } - - virtual unsigned resolve(uint8_t* dst) { - c.machineCode = dst; - c.assembler->setDestination(dst); - - Block* block = c.firstBlock; - while (block->nextBlock or block->nextInstruction) { - Block* next = block->nextBlock - ? block->nextBlock - : block->nextInstruction->firstEvent->block; - - next->start = block->assemblerBlock->resolve - (block->start, next->assemblerBlock); - - block = next; - } - - return c.machineCodeSize = block->assemblerBlock->resolve - (block->start, 0) + c.assembler->footerSize(); - } - - virtual unsigned poolSize() { - return c.constantCount * TargetBytesPerWord; - } - - virtual void write() { - c.assembler->write(); - - int i = 0; - for (ConstantPoolNode* n = c.firstConstant; n; n = n->next) { - target_intptr_t* target = reinterpret_cast - (c.machineCode + pad(c.machineCodeSize, TargetBytesPerWord) + i); - - if (n->promise->resolved()) { - *target = targetVW(n->promise->value()); - } else { - class Listener: public Promise::Listener { - public: - Listener(target_intptr_t* target): target(target){ } - - virtual bool resolve(int64_t value, void** location) { - *target = targetVW(value); - if (location) *location = target; - return true; - } - - target_intptr_t* target; - }; - new (n->promise->listen(sizeof(Listener))) Listener(target); - } - - i += TargetBytesPerWord; - } - } - - virtual void dispose() { - // ignore - } - - Context c; - local::Client client; -}; - -} // namespace local - -} // namespace - -namespace vm { - -Compiler* -makeCompiler(System* system, Assembler* assembler, Zone* zone, - Compiler::Client* client) -{ - return new(zone) local::MyCompiler(system, assembler, zone, client); -} - -} // namespace vm diff --git a/src/embed.cpp b/src/embed.cpp new file mode 100644 index 0000000000..e853087239 --- /dev/null +++ b/src/embed.cpp @@ -0,0 +1,133 @@ +/* Copyright (c) 2008-2012, 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 +#include +#include +#include +#include +#include + +#include "avian/embed.h" + +#ifdef __x86_64__ +# define BINARY_LOADER(x) _binary_loader_##x +#else +# define BINARY_LOADER(x) binary_loader_##x +#endif + +extern "C" const uint8_t BINARY_LOADER(start)[]; +extern "C" const uint8_t BINARY_LOADER(end)[]; + +__declspec(noreturn) +void printUsage(const wchar_t* executableName) +{ + wprintf(L"Usage: %s destination.exe classes.jar package.Main\n", executableName); + exit(0); +} + +void writeDestinationFile(const wchar_t* filename) +{ + if(FILE* file = _wfopen(filename, L"wb")) + { + size_t count = BINARY_LOADER(end) - BINARY_LOADER(start); + if(count == fwrite(BINARY_LOADER(start), sizeof(BINARY_LOADER(start)[0]), count, file)) + { + fclose(file); + return; + } + } + + fprintf(stderr, "Unable to write to destination file\n"); + exit(EXIT_FAILURE); +} + +void readFile(std::vector* jarFile, const wchar_t* fileName) +{ + if(FILE* file = _wfopen(fileName, L"rb")) + { + fseek(file, 0, SEEK_END); + jarFile->resize(ftell(file)); + fseek(file, 0, SEEK_SET); + fread(&jarFile->at(0), 1, jarFile->size(), file); + fclose(file); + } +} + +bool mkStringSection(std::vector* stringSection, const std::vector& strings, int first, int last) +{ + stringSection->clear(); + for(int i = first; i <= last; ++i) + { + const std::wstring& s = strings.at(i); + stringSection->push_back(s.size()); + stringSection->insert(stringSection->end(), s.begin(), s.end()); + } + + // pad to 16 entries + for(int i = last - first; i < 15; ++i) + stringSection->push_back(0); + + return stringSection->size() > 16; +} + +void writeStringResources(HANDLE hDest, const std::vector& strings) +{ + for(unsigned i = 0; i < strings.size(); i += 16) + { + std::vector stringSection; + + if(mkStringSection(&stringSection, strings, i, std::min(i + 15, strings.size() - 1))) + UpdateResourceW(hDest, reinterpret_cast(RT_STRING), reinterpret_cast(MAKEINTRESOURCE((i >> 4) + 1)), LANG_NEUTRAL, &stringSection.at(0), sizeof(wchar_t) * stringSection.size()); + } +} + +int wmain(int argc, wchar_t* argv[]) +{ + if(argc != 4) + printUsage(argv[0]); + + const wchar_t* destinationName = argv[1]; + const wchar_t* classesName = argv[2]; + const wchar_t* mainClassName = argv[3]; + + writeDestinationFile(destinationName); + + if(HANDLE hDest = BeginUpdateResourceW(destinationName, TRUE)) + { + std::vector strings; + strings.resize(RESID_MAIN_CLASS + 1); + strings.at(RESID_MAIN_CLASS) = mainClassName; + + writeStringResources(hDest, strings); + + std::vector jarFile; + readFile(&jarFile, classesName); + UpdateResourceW(hDest, reinterpret_cast(RT_RCDATA), RESID_BOOT_JAR, LANG_NEUTRAL, &jarFile.at(0), jarFile.size()); + + EndUpdateResource(hDest, FALSE); + } + + + return 0; +} + +#ifndef _MSC_VER +extern "C" int _CRT_glob; +extern "C" void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*); + +int main() +{ + wchar_t **enpv, **argv; + int argc, si = 0; + __wgetmainargs(&argc, &argv, &enpv, _CRT_glob, &si); + return wmain(argc, argv); +} +#endif diff --git a/src/embedded-loader.cpp b/src/embedded-loader.cpp new file mode 100644 index 0000000000..bea3bec57e --- /dev/null +++ b/src/embedded-loader.cpp @@ -0,0 +1,110 @@ +/* Copyright (c) 2008-2012, 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 +#include +#include + +#include "avian/embed.h" +#include "jni.h" + +#if (defined __MINGW32__) || (defined _MSC_VER) +# define EXPORT __declspec(dllexport) +# ifdef _MSC_VER +# define not ! +# endif +#else +# define EXPORT __attribute__ ((visibility("default"))) \ + __attribute__ ((used)) +#endif + +extern "C" { + // since we aren't linking against libstdc++, we must implement this + // ourselves: + void __cxa_pure_virtual(void) { abort(); } + + EXPORT const uint8_t* + bootJar(unsigned* size) + { + if(HRSRC hResInfo = FindResourceW(NULL, RESID_BOOT_JAR, reinterpret_cast(RT_RCDATA))) + { + if(HGLOBAL hRes = LoadResource(NULL, hResInfo)) + { + *size = SizeofResource(NULL, hResInfo); + return (const uint8_t*)LockResource(hRes); + } + } + + fprintf(stderr, "boot.jar resource not found\n"); + + *size = 0; + return NULL; + } +} // extern "C" + +static void getMainClass(char* pName, int maxLen) +{ + if(0 == LoadString(NULL, RESID_MAIN_CLASS, pName, maxLen)) + { + fprintf(stderr, "Main class not specified\n"); + strcpy(pName, "Main"); + } +} + +int +main(int ac, const char** av) +{ + JavaVMInitArgs vmArgs; + vmArgs.version = JNI_VERSION_1_2; + vmArgs.nOptions = 1; + vmArgs.ignoreUnrecognized = JNI_TRUE; + + JavaVMOption options[1]; + vmArgs.options = options; + + options[0].optionString = const_cast("-Xbootclasspath:[bootJar]"); + + JavaVM* vm; + void* env; + JNI_CreateJavaVM(&vm, &env, &vmArgs); + JNIEnv* e = static_cast(env); + + char mainClass[256]; + getMainClass(mainClass, sizeof(mainClass)); + + jclass c = e->FindClass(mainClass); + if (not e->ExceptionCheck()) { + jmethodID m = e->GetStaticMethodID(c, "main", "([Ljava/lang/String;)V"); + if (not e->ExceptionCheck()) { + jclass stringClass = e->FindClass("java/lang/String"); + if (not e->ExceptionCheck()) { + jobjectArray a = e->NewObjectArray(ac-1, stringClass, 0); + if (not e->ExceptionCheck()) { + for (int i = 1; i < ac; ++i) { + e->SetObjectArrayElement(a, i-1, e->NewStringUTF(av[i])); + } + + e->CallStaticVoidMethod(c, m, a); + } else fprintf(stderr, "Couldn't create array\n"); + } else fprintf(stderr, "java.lang.String not found\n"); + } else fprintf(stderr, "main method not found\n"); + } else fprintf(stderr, "Main class not found\n"); + + int exitCode = 0; + if(e->ExceptionCheck()) { + exitCode = -1; + e->ExceptionDescribe(); + e->ExceptionClear(); + } + + vm->DestroyJavaVM(); + + return exitCode; +} diff --git a/src/finder.cpp b/src/finder.cpp index 12c8edc8eb..7f4757803f 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -8,13 +8,17 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "zlib-custom.h" -#include "system.h" -#include "tokenizer.h" -#include "finder.h" -#include "lzma.h" +#include +#include +#include + +#include "avian/zlib-custom.h" +#include "avian/finder.h" +#include "avian/lzma.h" + using namespace vm; +using namespace avian::util; namespace { @@ -602,7 +606,7 @@ class BuiltinElement: public JarElement { } virtual const char* urlPrefix() { - return "avian_vm_resource:"; + return "avianvmresource:"; } virtual const char* sourceUrl() { @@ -656,12 +660,12 @@ addTokens(System* s, Element** first, Element** last, Allocator* allocator, const char* jarName, unsigned jarNameBase, const char* tokens, unsigned tokensLength, const char* bootLibrary) { - for (Tokenizer t(tokens, tokensLength, ' '); t.hasMore();) { - Tokenizer::Token token(t.next()); + for (Tokenizer t(String(tokens, tokensLength), ' '); t.hasMore();) { + String token(t.next()); RUNTIME_ARRAY(char, n, jarNameBase + token.length + 1); memcpy(RUNTIME_ARRAY_BODY(n), jarName, jarNameBase); - memcpy(RUNTIME_ARRAY_BODY(n) + jarNameBase, token.s, token.length); + memcpy(RUNTIME_ARRAY_BODY(n) + jarNameBase, token.text, token.length); RUNTIME_ARRAY_BODY(n)[jarNameBase + token.length] = 0; add(s, first, last, allocator, RUNTIME_ARRAY_BODY(n), @@ -811,9 +815,9 @@ parsePath(System* s, Allocator* allocator, const char* path, Element* first = 0; Element* last = 0; for (Tokenizer t(path, s->pathSeparator()); t.hasMore();) { - Tokenizer::Token token(t.next()); + String token(t.next()); - add(s, &first, &last, allocator, token.s, token.length, bootLibrary); + add(s, &first, &last, allocator, token.text, token.length, bootLibrary); } return first; diff --git a/src/heap.cpp b/src/heap/heap.cpp similarity index 90% rename from src/heap.cpp rename to src/heap/heap.cpp index 0bc37862fd..fac8cdb934 100644 --- a/src/heap.cpp +++ b/src/heap/heap.cpp @@ -8,12 +8,15 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "heap.h" -#include "system.h" -#include "common.h" -#include "arch.h" +#include +#include +#include "avian/common.h" +#include "avian/arch.h" + +#include using namespace vm; +using namespace avian::util; namespace { @@ -24,8 +27,6 @@ const unsigned Top = ~static_cast(0); const unsigned InitialGen2CapacityInBytes = 4 * 1024 * 1024; const unsigned InitialTenuredFixieCeilingInBytes = 4 * 1024 * 1024; -const unsigned LowMemoryPaddingInBytes = 1024 * 1024; - const bool Verbose = false; const bool Verbose2 = false; const bool Debug = false; @@ -55,13 +56,11 @@ class MutexLock { class Context; -void NO_RETURN abort(Context*); -#ifndef NDEBUG -void assert(Context*, bool); -#endif +Aborter* getAborter(Context* c); void* tryAllocate(Context* c, unsigned size); void* allocate(Context* c, unsigned size); +void* allocate(Context* c, unsigned size, bool limit); void free(Context* c, const void* p, unsigned size); #ifdef USE_ATOMIC_OPERATIONS @@ -80,13 +79,13 @@ markBitAtomic(uintptr_t* map, unsigned i) inline void* get(void* o, unsigned offsetInWords) { - return mask(cast(o, offsetInWords * BytesPerWord)); + return maskAlignedPointer(fieldAtOffset(o, offsetInWords * BytesPerWord)); } inline void** getp(void* o, unsigned offsetInWords) { - return &cast(o, offsetInWords * BytesPerWord); + return &fieldAtOffset(o, offsetInWords * BytesPerWord); } inline void @@ -223,7 +222,7 @@ class Segment { unsigned scale, unsigned bitsPerRecord) { unsigned result - = ceiling(ceiling(capacity, scale) * bitsPerRecord, BitsPerWord); + = ceilingDivide(ceilingDivide(capacity, scale) * bitsPerRecord, BitsPerWord); assert(c, result); return result; } @@ -331,7 +330,8 @@ class Segment { unsigned capacity_; Map* map; - Segment(Context* context, Map* map, unsigned desired, unsigned minimum): + Segment(Context* context, Map* map, unsigned desired, unsigned minimum, + int64_t available = INT64_MAX): context(context), data(0), position_(0), @@ -346,9 +346,41 @@ class Segment { assert(context, desired >= minimum); capacity_ = desired; + + if (static_cast(footprint(capacity_)) > available) { + unsigned top = capacity_; + unsigned bottom = minimum; + unsigned target = available; + while (true) { + if (static_cast(footprint(capacity_)) > target) { + if (bottom == capacity_) { + break; + } else if (static_cast(footprint(capacity_ - 1)) + <= target) + { + -- capacity_; + break; + } + top = capacity_; + capacity_ = avg(bottom, capacity_); + } else if (static_cast(footprint(capacity_)) < target) { + if (top == capacity_ + or static_cast(footprint(capacity_ + 1)) >= target) + { + break; + } + bottom = capacity_; + capacity_ = avg(top, capacity_); + } else { + break; + } + } + } + while (data == 0) { data = static_cast - (tryAllocate(context, (footprint(capacity_)) * BytesPerWord)); + (local::allocate + (context, (footprint(capacity_)) * BytesPerWord, false)); if (data == 0) { if (capacity_ > minimum) { @@ -531,7 +563,7 @@ class Fixie { } static unsigned maskSize(unsigned size, bool hasMask) { - return hasMask * ceiling(size, BitsPerWord) * BytesPerWord; + return hasMask * ceilingDivide(size, BitsPerWord) * BytesPerWord; } static unsigned totalSize(unsigned size, bool hasMask) { @@ -609,7 +641,6 @@ class Context { client(0), count(0), limit(limit), - lowMemoryThreshold(limit / 2), lock(0), immortalHeapStart(0), @@ -634,6 +665,7 @@ class Context { gen2Base(0), incomingFootprint(0), + pendingAllocation(0), tenureFootprint(0), gen1Padding(0), tenurePadding(0), @@ -654,7 +686,9 @@ class Context { lastCollectionTime(system->now()), totalCollectionTime(0), - totalTime(0) + totalTime(0), + + limitWasExceeded(false) { if (not system->success(system->make(&lock))) { system->abort(); @@ -680,7 +714,6 @@ class Context { unsigned count; unsigned limit; - unsigned lowMemoryThreshold; System::Mutex* lock; @@ -706,6 +739,7 @@ class Context { unsigned gen2Base; unsigned incomingFootprint; + int pendingAllocation; unsigned tenureFootprint; unsigned gen1Padding; unsigned tenurePadding; @@ -727,6 +761,8 @@ class Context { int64_t lastCollectionTime; int64_t totalCollectionTime; int64_t totalTime; + + bool limitWasExceeded; }; const char* @@ -745,20 +781,10 @@ segment(Context* c, void* p) } } -inline void NO_RETURN -abort(Context* c) -{ - abort(c->system); +inline Aborter* getAborter(Context* c) { + return c->system; } -#ifndef NDEBUG -inline void -assert(Context* c, bool v) -{ - assert(c->system, v); -} -#endif - inline unsigned minimumNextGen1Capacity(Context* c) { @@ -780,21 +806,6 @@ oversizedGen2(Context* c) and c->gen2.position() < (c->gen2.capacity() / 4); } -inline unsigned -memoryNeeded(Context* c) -{ - return c->count - + ((c->gen1.footprint(minimumNextGen1Capacity(c)) - + c->gen2.footprint(minimumNextGen2Capacity(c))) * BytesPerWord) - + LowMemoryPaddingInBytes; -} - -inline bool -lowMemory(Context* c) -{ - return memoryNeeded(c) > c->lowMemoryThreshold; -} - inline void initNextGen1(Context* c) { @@ -828,7 +839,7 @@ initNextGen2(Context* c) unsigned minimum = minimumNextGen2Capacity(c); unsigned desired = minimum; - if (not (lowMemory(c) or oversizedGen2(c))) { + if (not oversizedGen2(c)) { desired *= 2; } @@ -836,7 +847,13 @@ initNextGen2(Context* c) desired = InitialGen2CapacityInBytes / BytesPerWord; } - new (&(c->nextGen2)) Segment(c, &(c->nextHeapMap), desired, minimum); + new (&(c->nextGen2)) Segment + (c, &(c->nextHeapMap), desired, minimum, + static_cast(c->limit / BytesPerWord) + - (static_cast(c->count / BytesPerWord) + - c->gen2.footprint(c->gen2.capacity()) + - c->gen1.footprint(c->gen1.capacity()) + + c->pendingAllocation)); if (Verbose2) { fprintf(stderr, "init nextGen2 to %d bytes\n", @@ -862,21 +879,21 @@ inline void* follow(Context* c UNUSED, void* o) { assert(c, wasCollected(c, o)); - return cast(o, 0); + return fieldAtOffset(o, 0); } inline void*& parent(Context* c UNUSED, void* o) { assert(c, wasCollected(c, o)); - return cast(o, BytesPerWord); + return fieldAtOffset(o, BytesPerWord); } inline uintptr_t* bitset(Context* c UNUSED, void* o) { assert(c, wasCollected(c, o)); - return &cast(o, BytesPerWord * 2); + return &fieldAtOffset(o, BytesPerWord * 2); } void @@ -1059,7 +1076,7 @@ copy(Context* c, void* o) } // leave a pointer to the copy in the original - cast(o, 0) = r; + fieldAtOffset(o, 0) = r; return r; } @@ -1077,6 +1094,7 @@ update3(Context* c, void* o, bool* needsVisit) fprintf(stderr, "mark fixie %p\n", f); } f->marked(true); + f->dead(false); f->move(c, &(c->markedFixies)); } *needsVisit = false; @@ -1178,12 +1196,12 @@ updateHeapMap(Context* c, void* p, void* target, unsigned offset, void* result) void* update(Context* c, void** p, void* target, unsigned offset, bool* needsVisit) { - if (mask(*p) == 0) { + if (maskAlignedPointer(*p) == 0) { *needsVisit = false; return 0; } - void* result = update2(c, mask(*p), needsVisit); + void* result = update2(c, maskAlignedPointer(*p), needsVisit); if (result) { updateHeapMap(c, p, target, offset, result); @@ -1296,20 +1314,20 @@ bitsetNext(Context* c, uintptr_t* p) void collect(Context* c, void** p, void* target, unsigned offset) { - void* original = mask(*p); + void* original = maskAlignedPointer(*p); void* parent_ = 0; if (Debug) { fprintf(stderr, "update %p (%s) at %p (%s)\n", - mask(*p), segment(c, *p), p, segment(c, p)); + maskAlignedPointer(*p), segment(c, *p), p, segment(c, p)); } bool needsVisit; - local::set(p, update(c, mask(p), target, offset, &needsVisit)); + local::set(p, update(c, maskAlignedPointer(p), target, offset, &needsVisit)); if (Debug) { fprintf(stderr, " result: %p (%s) (visit? %d)\n", - mask(*p), segment(c, *p), needsVisit); + maskAlignedPointer(*p), segment(c, *p), needsVisit); } if (not needsVisit) return; @@ -1672,18 +1690,40 @@ collect2(Context* c) c->client->visitRoots(&v); } +bool +limitExceeded(Context* c, int pendingAllocation) +{ + unsigned count = c->count + pendingAllocation + - (c->gen2.remaining() * BytesPerWord); + + if (Verbose) { + if (count > c->limit) { + if (not c->limitWasExceeded) { + c->limitWasExceeded = true; + fprintf(stderr, "heap limit %d exceeded: %d\n", c->limit, count); + } + } else if (c->limitWasExceeded) { + c->limitWasExceeded = false; + fprintf(stderr, "heap limit %d no longer exceeded: %d\n", + c->limit, count); + } + } + + return count > c->limit; +} + void collect(Context* c) { - if (lowMemory(c) + if (limitExceeded(c, c->pendingAllocation) or oversizedGen2(c) or c->tenureFootprint + c->tenurePadding > c->gen2.remaining() or c->fixieTenureFootprint + c->tenuredFixieFootprint > c->tenuredFixieCeiling) { if (Verbose) { - if (lowMemory(c)) { - fprintf(stderr, "low memory causes "); + if (limitExceeded(c, c->pendingAllocation)) { + fprintf(stderr, "low memory causes "); } else if (oversizedGen2(c)) { fprintf(stderr, "oversized gen2 causes "); } else if (c->tenureFootprint + c->tenurePadding > c->gen2.remaining()) @@ -1708,25 +1748,6 @@ collect(Context* c) then = c->system->now(); } - unsigned count = memoryNeeded(c); - if (count > c->lowMemoryThreshold) { - if (Verbose) { - fprintf(stderr, "increase low memory threshold from %d to %d\n", - c->lowMemoryThreshold, - avg(c->limit, c->lowMemoryThreshold)); - } - - c->lowMemoryThreshold = avg(c->limit, c->lowMemoryThreshold); - } else if (count + (count / 16) < c->lowMemoryThreshold) { - if (Verbose) { - fprintf(stderr, "decrease low memory threshold from %d to %d\n", - c->lowMemoryThreshold, - avg(count, c->lowMemoryThreshold)); - } - - c->lowMemoryThreshold = avg(count, c->lowMemoryThreshold); - } - initNextGen1(c); if (c->mode == Heap::MajorCollection) { @@ -1867,8 +1888,12 @@ class MyHeap: public Heap { c.immortalHeapEnd = start + sizeInWords; } - virtual bool limitExceeded() { - return c.count > c.limit; + virtual unsigned limit() { + return c.limit; + } + + virtual bool limitExceeded(int pendingAllocation = 0) { + return local::limitExceeded(&c, pendingAllocation); } virtual void* tryAllocate(unsigned size) { @@ -1883,28 +1908,45 @@ class MyHeap: public Heap { free_(&c, p, size); } - virtual void collect(CollectionType type, unsigned incomingFootprint) { + virtual void collect(CollectionType type, unsigned incomingFootprint, + int pendingAllocation) + { c.mode = type; c.incomingFootprint = incomingFootprint; + c.pendingAllocation = pendingAllocation; local::collect(&c); } - virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords, - bool objectMask, unsigned* totalInBytes) + virtual unsigned fixedFootprint(unsigned sizeInWords, bool objectMask) { + return Fixie::totalSize(sizeInWords, objectMask); + } + + void* allocateFixed(Allocator* allocator, unsigned sizeInWords, + bool objectMask, Fixie** handle, bool immortal) { - *totalInBytes = Fixie::totalSize(sizeInWords, objectMask); - return (new (allocator->allocate(*totalInBytes)) - Fixie(&c, sizeInWords, objectMask, &(c.fixies), false))->body(); + expect(&c, not limitExceeded()); + + unsigned total = Fixie::totalSize(sizeInWords, objectMask); + void* p = allocator->allocate(total); + + expect(&c, not limitExceeded()); + + return (new (p) Fixie(&c, sizeInWords, objectMask, handle, immortal)) + ->body(); + } + + virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords, + bool objectMask) + { + return allocateFixed + (allocator, sizeInWords, objectMask, &(c.fixies), false); } virtual void* allocateImmortalFixed(Allocator* allocator, - unsigned sizeInWords, bool objectMask, - unsigned* totalInBytes) + unsigned sizeInWords, bool objectMask) { - *totalInBytes = Fixie::totalSize(sizeInWords, objectMask); - return (new (allocator->allocate(*totalInBytes)) - Fixie(&c, sizeInWords, objectMask, 0, true))->body(); + return allocateFixed(allocator, sizeInWords, objectMask, 0, true); } bool needsMark(void* p) { @@ -1939,10 +1981,10 @@ class MyHeap: public Heap { bool dirty = false; for (unsigned i = 0; i < count; ++i) { void** target = static_cast(p) + offset + i; - if (targetNeedsMark(mask(*target))) { + if (targetNeedsMark(maskAlignedPointer(*target))) { if (DebugFixies) { fprintf(stderr, "dirty fixie %p at %d (%p): %p\n", - f, offset, f->body() + offset, mask(*target)); + f, offset, f->body() + offset, maskAlignedPointer(*target)); } dirty = true; @@ -1967,7 +2009,7 @@ class MyHeap: public Heap { for (unsigned i = 0; i < count; ++i) { void** target = static_cast(p) + offset + i; - if (targetNeedsMark(mask(*target))) { + if (targetNeedsMark(maskAlignedPointer(*target))) { #ifdef USE_ATOMIC_OPERATIONS map->markAtomic(target); #else @@ -2014,7 +2056,7 @@ class MyHeap: public Heap { } virtual Status status(void* p) { - p = mask(p); + p = maskAlignedPointer(p); if (p == 0) { return Null; diff --git a/src/heapdump.cpp b/src/heapdump.cpp index da283e61a1..b68a22b6c7 100644 --- a/src/heapdump.cpp +++ b/src/heapdump.cpp @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "heapwalk.h" +#include "avian/machine.h" +#include "avian/heapwalk.h" using namespace vm; diff --git a/src/heapwalk.cpp b/src/heapwalk.cpp index 811382a825..977ab85bac 100644 --- a/src/heapwalk.cpp +++ b/src/heapwalk.cpp @@ -8,8 +8,8 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "machine.h" -#include "heapwalk.h" +#include "avian/machine.h" +#include "avian/heapwalk.h" using namespace vm; @@ -228,7 +228,8 @@ inline object get(object o, unsigned offsetInWords) { return static_cast - (mask(cast(o, offsetInWords * BytesPerWord))); + (maskAlignedPointer + (fieldAtOffset(o, offsetInWords * BytesPerWord))); } unsigned @@ -297,7 +298,8 @@ class MyHeapWalker: public HeapWalker { Visitor(Context* c, HeapVisitor* v): c(c), v(v) { } virtual void visit(void* p) { - walk(c, v, static_cast(mask(*static_cast(p)))); + walk(c, v, static_cast + (maskAlignedPointer(*static_cast(p)))); } Context* c; diff --git a/src/interpret.cpp b/src/interpret.cpp index da3e6c7853..d222d4c1b5 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -8,17 +8,19 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "common.h" -#include "system.h" -#include "constants.h" -#include "machine.h" -#include "processor.h" -#include "process.h" -#include "arch.h" +#include "avian/common.h" +#include +#include "avian/constants.h" +#include "avian/machine.h" +#include "avian/processor.h" +#include "avian/process.h" +#include "avian/arch.h" + +#include using namespace vm; -namespace { +namespace local { const unsigned FrameBaseOffset = 0; const unsigned FrameNextOffset = 1; @@ -92,7 +94,7 @@ inline void pushLong(Thread* t, uint64_t v) { if (DebugStack) { - fprintf(stderr, "push long %"LLD" at %d\n", v, t->sp); + fprintf(stderr, "push long %" LLD " at %d\n", v, t->sp); } pushInt(t, v >> 32); @@ -123,7 +125,7 @@ inline uint32_t popInt(Thread* t) { if (DebugStack) { - fprintf(stderr, "pop int %"ULD" at %d\n", + fprintf(stderr, "pop int %" ULD " at %d\n", t->stack[((t->sp - 1) * 2) + 1], t->sp - 1); } @@ -142,7 +144,7 @@ inline uint64_t popLong(Thread* t) { if (DebugStack) { - fprintf(stderr, "pop long %"LLD" at %d\n", + fprintf(stderr, "pop long %" LLD " at %d\n", (static_cast(t->stack[((t->sp - 2) * 2) + 1]) << 32) | static_cast(t->stack[((t->sp - 1) * 2) + 1]), t->sp - 2); @@ -178,7 +180,7 @@ inline uint32_t peekInt(Thread* t, unsigned index) { if (DebugStack) { - fprintf(stderr, "peek int %"ULD" at %d\n", + fprintf(stderr, "peek int %" ULD " at %d\n", t->stack[(index * 2) + 1], index); } @@ -192,7 +194,7 @@ inline uint64_t peekLong(Thread* t, unsigned index) { if (DebugStack) { - fprintf(stderr, "peek long %"LLD" at %d\n", + fprintf(stderr, "peek long %" LLD " at %d\n", (static_cast(t->stack[(index * 2) + 1]) << 32) | static_cast(t->stack[((index + 1) * 2) + 1]), index); @@ -228,7 +230,7 @@ inline void pokeLong(Thread* t, unsigned index, uint64_t value) { if (DebugStack) { - fprintf(stderr, "poke long %"LLD" at %d\n", value, index); + fprintf(stderr, "poke long %" LLD " at %d\n", value, index); } pokeInt(t, index, value >> 32); @@ -461,7 +463,7 @@ pushResult(Thread* t, unsigned returnCode, uint64_t result, bool indirect) case DoubleField: case LongField: if (DebugRun) { - fprintf(stderr, "result: %"LLD"\n", result); + fprintf(stderr, "result: %" LLD "\n", result); } pushLong(t, result); break; @@ -647,7 +649,7 @@ invokeNative(Thread* t, object method) { THREAD_RESOURCE0(t, popFrame(static_cast(t))); unsigned footprint = methodParameterFootprint(t, method); - RUNTIME_ARRAY(uintptr_t, args, footprint); + THREAD_RUNTIME_ARRAY(t, uintptr_t, args, footprint); unsigned sp = frameBase(t, t->frame); unsigned argOffset = 0; if ((methodFlags(t, method) & ACC_STATIC) == 0) { @@ -678,6 +680,18 @@ store(Thread* t, unsigned index) BytesPerWord * 2); } +bool +isNaN(double v) +{ + return fpclassify(v) == FP_NAN; +} + +bool +isNaN(float v) +{ + return fpclassify(v) == FP_NAN; +} + uint64_t findExceptionHandler(Thread* t, object method, unsigned ip) { @@ -733,26 +747,26 @@ pushField(Thread* t, object target, object field) switch (fieldCode(t, field)) { case ByteField: case BooleanField: - pushInt(t, cast(target, fieldOffset(t, field))); + pushInt(t, fieldAtOffset(target, fieldOffset(t, field))); break; case CharField: case ShortField: - pushInt(t, cast(target, fieldOffset(t, field))); + pushInt(t, fieldAtOffset(target, fieldOffset(t, field))); break; case FloatField: case IntField: - pushInt(t, cast(target, fieldOffset(t, field))); + pushInt(t, fieldAtOffset(target, fieldOffset(t, field))); break; case DoubleField: case LongField: - pushLong(t, cast(target, fieldOffset(t, field))); + pushLong(t, fieldAtOffset(target, fieldOffset(t, field))); break; case ObjectField: - pushObject(t, cast(target, fieldOffset(t, field))); + pushObject(t, fieldAtOffset(target, fieldOffset(t, field))); break; default: @@ -902,7 +916,7 @@ interpret3(Thread* t, const int base) case arraylength: { object array = popObject(t); if (LIKELY(array)) { - pushInt(t, cast(array, BytesPerWord)); + pushInt(t, fieldAtOffset(array, BytesPerWord)); } else { exception = makeThrowable(t, Machine::NullPointerExceptionType); goto throw_; @@ -1155,7 +1169,9 @@ interpret3(Thread* t, const int base) double b = popDouble(t); double a = popDouble(t); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + pushInt(t, 1); + } if (a < b) { pushInt(t, static_cast(-1)); } else if (a > b) { pushInt(t, 1); @@ -1170,7 +1186,9 @@ interpret3(Thread* t, const int base) double b = popDouble(t); double a = popDouble(t); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + pushInt(t, static_cast(-1)); + } if (a < b) { pushInt(t, static_cast(-1)); } else if (a > b) { pushInt(t, 1); @@ -1368,7 +1386,9 @@ interpret3(Thread* t, const int base) float b = popFloat(t); float a = popFloat(t); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + pushInt(t, 1); + } if (a < b) { pushInt(t, static_cast(-1)); } else if (a > b) { pushInt(t, 1); @@ -1383,7 +1403,9 @@ interpret3(Thread* t, const int base) float b = popFloat(t); float a = popFloat(t); - if (a < b) { + if (isNaN(a) or isNaN(b)) { + pushInt(t, static_cast(-1)); + } if (a < b) { pushInt(t, static_cast(-1)); } else if (a > b) { pushInt(t, 1); @@ -2321,21 +2343,22 @@ interpret3(Thread* t, const int base) object class_ = resolveClassInPool(t, frameMethod(t, frame), index - 1); PROTECT(t, class_); - int32_t counts[dimensions]; + THREAD_RUNTIME_ARRAY(t, int32_t, counts, dimensions); for (int i = dimensions - 1; i >= 0; --i) { - counts[i] = popInt(t); - if (UNLIKELY(counts[i] < 0)) { + RUNTIME_ARRAY_BODY(counts)[i] = popInt(t); + if (UNLIKELY(RUNTIME_ARRAY_BODY(counts)[i] < 0)) { exception = makeThrowable - (t, Machine::NegativeArraySizeExceptionType, "%d", counts[i]); + (t, Machine::NegativeArraySizeExceptionType, "%d", + RUNTIME_ARRAY_BODY(counts)[i]); goto throw_; } } - object array = makeArray(t, counts[0]); + object array = makeArray(t, RUNTIME_ARRAY_BODY(counts)[0]); setObjectClass(t, array, class_); PROTECT(t, array); - populateMultiArray(t, array, counts, 0, dimensions); + populateMultiArray(t, array, RUNTIME_ARRAY_BODY(counts), 0, dimensions); pushObject(t, array); } goto loop; @@ -2436,17 +2459,17 @@ interpret3(Thread* t, const int base) switch (fieldCode(t, field)) { case ByteField: case BooleanField: - cast(o, fieldOffset(t, field)) = value; + fieldAtOffset(o, fieldOffset(t, field)) = value; break; case CharField: case ShortField: - cast(o, fieldOffset(t, field)) = value; + fieldAtOffset(o, fieldOffset(t, field)) = value; break; case FloatField: case IntField: - cast(o, fieldOffset(t, field)) = value; + fieldAtOffset(o, fieldOffset(t, field)) = value; break; } } else { @@ -2459,7 +2482,7 @@ interpret3(Thread* t, const int base) int64_t value = popLong(t); object o = popObject(t); if (LIKELY(o)) { - cast(o, fieldOffset(t, field)) = value; + fieldAtOffset(o, fieldOffset(t, field)) = value; } else { exception = makeThrowable(t, Machine::NullPointerExceptionType); } @@ -2510,24 +2533,24 @@ interpret3(Thread* t, const int base) switch (fieldCode(t, field)) { case ByteField: case BooleanField: - cast(table, fieldOffset(t, field)) = value; + fieldAtOffset(table, fieldOffset(t, field)) = value; break; case CharField: case ShortField: - cast(table, fieldOffset(t, field)) = value; + fieldAtOffset(table, fieldOffset(t, field)) = value; break; case FloatField: case IntField: - cast(table, fieldOffset(t, field)) = value; + fieldAtOffset(table, fieldOffset(t, field)) = value; break; } } break; case DoubleField: case LongField: { - cast(table, fieldOffset(t, field)) = popLong(t); + fieldAtOffset(table, fieldOffset(t, field)) = popLong(t); } break; case ObjectField: { @@ -2823,7 +2846,7 @@ pushArguments(Thread* t, object this_, const char* spec, break; case 'F': { - pushFloat(t, arguments[index++].d); + pushFloat(t, arguments[index++].f); } break; default: @@ -2850,11 +2873,11 @@ pushArguments(Thread* t, object this_, const char* spec, object a) case 'J': case 'D': - pushLong(t, cast(objectArrayBody(t, a, index++), 8)); + pushLong(t, fieldAtOffset(objectArrayBody(t, a, index++), 8)); break; default: - pushInt(t, cast(objectArrayBody(t, a, index++), + pushInt(t, fieldAtOffset(objectArrayBody(t, a, index++), BytesPerWord)); break; } @@ -3100,7 +3123,7 @@ class MyProcessor: public Processor { (&byteArrayBody(t, methodSpec(t, method), 0)); pushArguments(t, this_, spec, arguments); - return ::invoke(t, method); + return local::invoke(t, method); } virtual object @@ -3124,7 +3147,7 @@ class MyProcessor: public Processor { (&byteArrayBody(t, methodSpec(t, method), 0)); pushArguments(t, this_, spec, arguments); - return ::invoke(t, method); + return local::invoke(t, method); } virtual object @@ -3148,7 +3171,7 @@ class MyProcessor: public Processor { (&byteArrayBody(t, methodSpec(t, method), 0)); pushArguments(t, this_, spec, indirectObjects, arguments); - return ::invoke(t, method); + return local::invoke(t, method); } virtual object @@ -3174,7 +3197,7 @@ class MyProcessor: public Processor { assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); - return ::invoke(t, method); + return local::invoke(t, method); } virtual object getStackTrace(vm::Thread* t, vm::Thread*) { @@ -3191,7 +3214,7 @@ class MyProcessor: public Processor { } virtual void compileMethod(vm::Thread*, Zone*, object*, object*, - DelayedPromise**, object, OffsetResolver*) + avian::codegen::DelayedPromise**, object, OffsetResolver*) { abort(s); } @@ -3254,8 +3277,8 @@ namespace vm { Processor* makeProcessor(System* system, Allocator* allocator, bool) { - return new (allocator->allocate(sizeof(MyProcessor))) - MyProcessor(system, allocator); + return new (allocator->allocate(sizeof(local::MyProcessor))) + local::MyProcessor(system, allocator); } } // namespace vm diff --git a/src/jnienv.cpp b/src/jnienv.cpp index e4abe152f5..5ae0dd7a7a 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -8,11 +8,13 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "jnienv.h" -#include "machine.h" -#include "util.h" -#include "processor.h" -#include "constants.h" +#include "avian/jnienv.h" +#include "avian/machine.h" +#include "avian/util.h" +#include "avian/processor.h" +#include "avian/constants.h" + +#include using namespace vm; @@ -45,19 +47,23 @@ DetachCurrentThread(Machine* m) { Thread* t = static_cast(m->localThread->get()); if (t) { - expect(t, t != m->rootThread); + // todo: detaching the root thread seems to cause stability + // problems which I haven't yet had a chance to investigate + // thoroughly. Meanwhile, we just ignore requests to detach it, + // which leaks a bit of memory but should be harmless otherwise. + if (m->rootThread != t) { + m->localThread->set(0); - m->localThread->set(0); + ACQUIRE_RAW(t, t->m->stateLock); - ACQUIRE_RAW(t, t->m->stateLock); + enter(t, Thread::ActiveState); - enter(t, Thread::ActiveState); + threadPeer(t, t->javaThread) = 0; - threadPeer(t, t->javaThread) = 0; + enter(t, Thread::ZombieState); - enter(t, Thread::ZombieState); - - t->state = Thread::JoinedState; + t->state = Thread::JoinedState; + } return 0; } else { @@ -68,11 +74,18 @@ DetachCurrentThread(Machine* m) uint64_t destroyJavaVM(Thread* t, uintptr_t*) { + { ENTER(t, Thread::ActiveState); + + t->m->classpath->shutDown(t); + } + // wait for other non-daemon threads to exit { ACQUIRE(t, t->m->stateLock); while (t->m->liveCount - t->m->daemonCount > 1) { t->m->stateLock->wait(t->systemThread, 0); } + + enter(t, Thread::ExclusiveState); } shutDown(t); @@ -98,7 +111,7 @@ GetEnv(Machine* m, Thread** t, jint version) { *t = static_cast(m->localThread->get()); if (*t) { - if (version <= JNI_VERSION_1_4) { + if (version <= JNI_VERSION_1_6) { return JNI_OK; } else { return JNI_EVERSION; @@ -156,10 +169,12 @@ GetStringRegion(Thread* t, jstring s, jsize start, jsize length, jchar* dst) const jchar* JNICALL GetStringCritical(Thread* t, jstring s, jboolean* isCopy) { - if ((t->criticalLevel ++) == 0) { + if (t->criticalLevel == 0) { enter(t, Thread::ActiveState); } + ++ t->criticalLevel; + if (isCopy) { *isCopy = true; } @@ -211,7 +226,7 @@ ReleaseStringUTFChars(Thread* t, jstring s, const char* chars) { ENTER(t, Thread::ActiveState); - t->m->heap->free(chars, stringLength(t, *s) + 1); + t->m->heap->free(chars, stringUTFLength(t, *s) + 1); } void JNICALL @@ -228,7 +243,7 @@ GetArrayLength(Thread* t, jarray array) { ENTER(t, Thread::ActiveState); - return cast(*array, BytesPerWord); + return fieldAtOffset(*array, BytesPerWord); } uint64_t @@ -237,9 +252,8 @@ newString(Thread* t, uintptr_t* arguments) const jchar* chars = reinterpret_cast(arguments[0]); jsize size = arguments[1]; - object a = 0; + object a = makeCharArray(t, size); if (size) { - a = makeCharArray(t, size); memcpy(&charArrayBody(t, a, 0), chars, size * sizeof(jchar)); } @@ -268,7 +282,7 @@ newStringUTF(Thread* t, uintptr_t* arguments) return reinterpret_cast (makeLocalReference (t, t->m->classpath->makeString - (t, array, 0, cast(array, BytesPerWord) - 1))); + (t, array, 0, fieldAtOffset(array, BytesPerWord) - 1))); } jstring JNICALL @@ -393,6 +407,14 @@ Throw(Thread* t, jthrowable throwable) return 0; } +jobject JNICALL +NewLocalRef(Thread* t, jobject o) +{ + ENTER(t, Thread::ActiveState); + + return makeLocalReference(t, *o); +} + void JNICALL DeleteLocalRef(Thread* t, jobject r) { @@ -427,12 +449,14 @@ GetObjectClass(Thread* t, jobject o) uint64_t getSuperclass(Thread* t, uintptr_t* arguments) { - jclass c = reinterpret_cast(arguments[0]); - - object super = classSuper(t, jclassVmClass(t, *c)); - - return super ? reinterpret_cast - (makeLocalReference(t, getJClass(t, super))) : 0; + object class_ = jclassVmClass(t, *reinterpret_cast(arguments[0])); + if (classFlags(t, class_) & ACC_INTERFACE) { + return 0; + } else { + object super = classSuper(t, class_); + return super ? reinterpret_cast + (makeLocalReference(t, getJClass(t, super))) : 0; + } } jclass JNICALL @@ -1519,7 +1543,7 @@ getObjectField(Thread* t, uintptr_t* arguments) ACQUIRE_FIELD_FOR_READ(t, field); return reinterpret_cast - (makeLocalReference(t, cast(*o, fieldOffset(t, field)))); + (makeLocalReference(t, fieldAtOffset(*o, fieldOffset(t, field)))); } jobject JNICALL @@ -1540,7 +1564,7 @@ getBooleanField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jboolean JNICALL @@ -1561,7 +1585,7 @@ getByteField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jbyte JNICALL @@ -1582,7 +1606,7 @@ getCharField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jchar JNICALL @@ -1603,7 +1627,7 @@ getShortField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jshort JNICALL @@ -1624,7 +1648,7 @@ getIntField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jint JNICALL @@ -1645,7 +1669,7 @@ getLongField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast(*o, fieldOffset(t, field)); + return fieldAtOffset(*o, fieldOffset(t, field)); } jlong JNICALL @@ -1666,7 +1690,7 @@ getFloatField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return floatToBits(cast(*o, fieldOffset(t, field))); + return floatToBits(fieldAtOffset(*o, fieldOffset(t, field))); } jfloat JNICALL @@ -1687,7 +1711,7 @@ getDoubleField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return doubleToBits(cast(*o, fieldOffset(t, field))); + return doubleToBits(fieldAtOffset(*o, fieldOffset(t, field))); } jdouble JNICALL @@ -1734,7 +1758,7 @@ setBooleanField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1759,7 +1783,7 @@ setByteField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1784,7 +1808,7 @@ setCharField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1809,7 +1833,7 @@ setShortField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1834,7 +1858,7 @@ setIntField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1859,7 +1883,7 @@ setLongField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1885,7 +1909,7 @@ setFloatField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1910,7 +1934,7 @@ setDoubleField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast(*o, fieldOffset(t, field)) = v; + fieldAtOffset(*o, fieldOffset(t, field)) = v; return 1; } @@ -1952,7 +1976,7 @@ getStaticObjectField(Thread* t, uintptr_t* arguments) return reinterpret_cast (makeLocalReference - (t, cast + (t, fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)))); } @@ -1977,7 +2001,7 @@ getStaticBooleanField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2002,7 +2026,7 @@ getStaticByteField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2027,7 +2051,7 @@ getStaticCharField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2052,7 +2076,7 @@ getStaticShortField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2077,7 +2101,7 @@ getStaticIntField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2102,7 +2126,7 @@ getStaticLongField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_READ(t, field); - return cast + return fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)); } @@ -2128,7 +2152,7 @@ getStaticFloatField(Thread* t, uintptr_t* arguments) ACQUIRE_FIELD_FOR_READ(t, field); return floatToBits - (cast + (fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field))); } @@ -2154,7 +2178,7 @@ getStaticDoubleField(Thread* t, uintptr_t* arguments) ACQUIRE_FIELD_FOR_READ(t, field); return doubleToBits - (cast + (fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field))); } @@ -2209,7 +2233,7 @@ setStaticBooleanField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2238,7 +2262,7 @@ setStaticByteField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2267,7 +2291,7 @@ setStaticCharField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2296,7 +2320,7 @@ setStaticShortField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2325,7 +2349,7 @@ setStaticIntField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2354,7 +2378,7 @@ setStaticLongField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2384,7 +2408,7 @@ setStaticFloatField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -2413,7 +2437,7 @@ setStaticDoubleField(Thread* t, uintptr_t* arguments) PROTECT(t, field); ACQUIRE_FIELD_FOR_WRITE(t, field); - cast + fieldAtOffset (classStaticTable(t, jclassVmClass(t, *c)), fieldOffset(t, field)) = v; return 1; @@ -3128,9 +3152,11 @@ SetDoubleArrayRegion(Thread* t, jdoubleArray array, jint offset, jint length, void* JNICALL GetPrimitiveArrayCritical(Thread* t, jarray array, jboolean* isCopy) { - if ((t->criticalLevel ++) == 0) { + if (t->criticalLevel == 0) { enter(t, Thread::ActiveState); } + + ++ t->criticalLevel; if (isCopy) { *isCopy = true; @@ -3149,6 +3175,82 @@ ReleasePrimitiveArrayCritical(Thread* t, jarray, void*, jint) } } +uint64_t +fromReflectedMethod(Thread* t, uintptr_t* arguments) +{ + jobject m = reinterpret_cast(arguments[0]); + + return methodID(t, t->m->classpath->getVMMethod(t, *m)); +} + +jmethodID JNICALL +FromReflectedMethod(Thread* t, jobject method) +{ + uintptr_t arguments[] = { reinterpret_cast(method) }; + + return static_cast(run(t, fromReflectedMethod, arguments)); +} + +uint64_t +toReflectedMethod(Thread* t, uintptr_t* arguments) +{ + jmethodID m = arguments[1]; + jboolean isStatic = arguments[2]; + + return reinterpret_cast + (makeLocalReference + (t, t->m->classpath->makeJMethod + (t, isStatic ? getStaticMethod(t, m) : getMethod(t, m)))); +} + +jobject JNICALL +ToReflectedMethod(Thread* t, jclass c, jmethodID method, jboolean isStatic) +{ + uintptr_t arguments[] = { reinterpret_cast(c), + static_cast(method), + static_cast(isStatic) }; + + return reinterpret_cast(run(t, toReflectedMethod, arguments)); +} + +uint64_t +fromReflectedField(Thread* t, uintptr_t* arguments) +{ + jobject f = reinterpret_cast(arguments[0]); + + return fieldID(t, t->m->classpath->getVMField(t, *f)); +} + +jfieldID JNICALL +FromReflectedField(Thread* t, jobject field) +{ + uintptr_t arguments[] = { reinterpret_cast(field) }; + + return static_cast(run(t, fromReflectedField, arguments)); +} + +uint64_t +toReflectedField(Thread* t, uintptr_t* arguments) +{ + jfieldID f = arguments[1]; + jboolean isStatic = arguments[2]; + + return reinterpret_cast + (makeLocalReference + (t, t->m->classpath->makeJField + (t, isStatic ? getStaticField(t, f) : getField(t, f)))); +} + +jobject JNICALL +ToReflectedField(Thread* t, jclass c, jfieldID field, jboolean isStatic) +{ + uintptr_t arguments[] = { reinterpret_cast(c), + static_cast(field), + static_cast(isStatic) }; + + return reinterpret_cast(run(t, toReflectedField, arguments)); +} + uint64_t registerNatives(Thread* t, uintptr_t* arguments) { @@ -3159,13 +3261,21 @@ registerNatives(Thread* t, uintptr_t* arguments) for (int i = 0; i < methodCount; ++i) { if (methods[i].function) { + // Android's class library sometimes prepends a mysterious "!" + // to the method signature, which we happily ignore: + const char* sig = methods[i].signature; + if (*sig == '!') ++ sig; + object method = findMethodOrNull - (t, jclassVmClass(t, *c), methods[i].name, methods[i].signature); + (t, jclassVmClass(t, *c), methods[i].name, sig); if (method == 0 or (methodFlags(t, method) & ACC_NATIVE) == 0) { // The JNI spec says we must throw a NoSuchMethodError in this // case, but that would prevent using a code shrinker like // ProGuard effectively. Instead, we just ignore it. + + // fprintf(stderr, "not found: %s.%s%s\n", &byteArrayBody(t, className(t, jclassVmClass(t, *c)), 0), methods[i].name, sig); + // abort(t); } else { registerNative(t, method, methods[i].function); } @@ -3281,12 +3391,21 @@ PushLocalFrame(Thread* t, jint capacity) uint64_t popLocalFrame(Thread* t, uintptr_t* arguments) { - object result = *reinterpret_cast(arguments[0]); - PROTECT(t, result); + uint64_t r; + jobject presult = reinterpret_cast(arguments[0]); + if(presult != NULL) { + object result = *presult; + PROTECT(t, result); - t->m->processor->popLocalFrame(t); - - return reinterpret_cast(makeLocalReference(t, result)); + t->m->processor->popLocalFrame(t); + + r = reinterpret_cast(makeLocalReference(t, result)); + } else { + t->m->processor->popLocalFrame(t); + r = 0; + } + + return r; } jobject JNICALL @@ -3297,27 +3416,56 @@ PopLocalFrame(Thread* t, jobject result) return reinterpret_cast(run(t, popLocalFrame, arguments)); } +uint64_t +newDirectByteBuffer(Thread* t, uintptr_t* arguments) +{ + jlong capacity; memcpy(&capacity, arguments + 1, sizeof(jlong)); + + return reinterpret_cast + (makeLocalReference + (t, t->m->classpath->makeDirectByteBuffer + (t, reinterpret_cast(arguments[0]), capacity))); +} + jobject JNICALL NewDirectByteBuffer(Thread* t, void* p, jlong capacity) { - jclass c = FindClass(t, "java/nio/DirectByteBuffer"); - return NewObject(t, c, GetMethodID(t, c, "", "(JI)V"), - reinterpret_cast(p), - static_cast(capacity)); + uintptr_t arguments[1 + (sizeof(jlong) / BytesPerWord)]; + arguments[0] = reinterpret_cast(p); + memcpy(arguments + 1, &capacity, sizeof(jlong)); + + return reinterpret_cast(run(t, newDirectByteBuffer, arguments)); +} + +uint64_t +getDirectBufferAddress(Thread* t, uintptr_t* arguments) +{ + return reinterpret_cast + (t->m->classpath->getDirectBufferAddress + (t, *reinterpret_cast(arguments[0]))); } void* JNICALL GetDirectBufferAddress(Thread* t, jobject b) { - return reinterpret_cast - (GetLongField(t, b, GetFieldID(t, GetObjectClass(t, b), "address", "J"))); + uintptr_t arguments[] = { reinterpret_cast(b) }; + + return reinterpret_cast(run(t, getDirectBufferAddress, arguments)); +} + +uint64_t +getDirectBufferCapacity(Thread* t, uintptr_t* arguments) +{ + return t->m->classpath->getDirectBufferCapacity + (t, *reinterpret_cast(arguments[0])); } jlong JNICALL GetDirectBufferCapacity(Thread* t, jobject b) { - return GetIntField - (t, b, GetFieldID(t, GetObjectClass(t, b), "capacity", "I")); + uintptr_t arguments[] = { reinterpret_cast(b) }; + + return run(t, getDirectBufferCapacity, arguments); } struct JavaVMOption { @@ -3340,11 +3488,11 @@ parseSize(const char* s) RUNTIME_ARRAY(char, buffer, length + 1); if (length == 0) { return 0; - } else if (s[length - 1] == 'k') { + } else if (s[length - 1] == 'k' or s[length - 1] == 'K') { memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1); RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0; return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024; - } else if (s[length - 1] == 'm') { + } else if (s[length - 1] == 'm' or s[length - 1] == 'M') { memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1); RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0; return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024 * 1024; @@ -3380,12 +3528,30 @@ boot(Thread* t, uintptr_t*) setRoot(t, Machine::Shutdown, makeThrowable(t, Machine::ThrowableType)); + t->m->classpath->preBoot(t); + + t->javaThread = t->m->classpath->makeThread(t, 0); + + threadPeer(t, t->javaThread) = reinterpret_cast(t); + setRoot(t, Machine::FinalizerThread, t->m->classpath->makeThread(t, t)); threadDaemon(t, root(t, Machine::FinalizerThread)) = true; t->m->classpath->boot(t); + const char* port = findProperty(t, "avian.trace.port"); + if (port) { + object host = makeString(t, "0.0.0.0"); + PROTECT(t, host); + + object method = resolveMethod + (t, root(t, Machine::BootLoader), "avian/Traces", "startTraceListener", + "(Ljava/lang/String;I)V"); + + t->m->processor->invoke(t, method, 0, host, atoi(port)); + } + enter(t, Thread::IdleState); return 1; @@ -3432,6 +3598,7 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) envTable->NewDirectByteBuffer = local::NewDirectByteBuffer; envTable->GetDirectBufferAddress = local::GetDirectBufferAddress; envTable->GetDirectBufferCapacity = local::GetDirectBufferCapacity; + envTable->NewLocalRef = local::NewLocalRef; envTable->DeleteLocalRef = local::DeleteLocalRef; envTable->GetObjectClass = local::GetObjectClass; envTable->GetSuperclass = local::GetSuperclass; @@ -3602,26 +3769,27 @@ populateJNITables(JavaVMVTable* vmTable, JNIEnvVTable* envTable) envTable->IsSameObject = local::IsSameObject; envTable->PushLocalFrame = local::PushLocalFrame; envTable->PopLocalFrame = local::PopLocalFrame; + envTable->FromReflectedMethod = local::FromReflectedMethod; + envTable->ToReflectedMethod = local::ToReflectedMethod; + envTable->FromReflectedField = local::FromReflectedField; + envTable->ToReflectedField = local::ToReflectedField; } } // namespace vm -#define BOOTSTRAP_PROPERTY "avian.bootstrap" -#define CRASHDIR_PROPERTY "avian.crash.dir" -#define EMBED_PREFIX_PROPERTY "avian.embed.prefix" -#define CLASSPATH_PROPERTY "java.class.path" -#define JAVA_HOME_PROPERTY "java.home" -#define BOOTCLASSPATH_PREPEND_OPTION "bootclasspath/p" -#define BOOTCLASSPATH_OPTION "bootclasspath" -#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" -#define BOOTCLASSPATH_APPEND_OPTION "bootclasspath/a" - extern "C" JNIEXPORT jint JNICALL JNI_GetDefaultJavaVMInitArgs(void*) { return 0; } +extern "C" JNIEXPORT jint JNICALL +JNI_GetCreatedJavaVMs(Machine**, jsize, jsize*) +{ + // todo + return -1; +} + extern "C" JNIEXPORT jint JNICALL JNI_CreateJavaVM(Machine** m, Thread** t, void* args) { @@ -3629,7 +3797,7 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) unsigned heapLimit = 0; unsigned stackLimit = 0; - const char* bootLibrary = 0; + const char* bootLibraries = 0; const char* classpath = 0; const char* javaHome = AVIAN_JAVA_HOME; const char* embedPrefix = AVIAN_EMBED_PREFIX; @@ -3665,7 +3833,15 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) if (strncmp(p, BOOTSTRAP_PROPERTY "=", sizeof(BOOTSTRAP_PROPERTY)) == 0) { - bootLibrary = p + sizeof(BOOTSTRAP_PROPERTY); + bootLibraries = p + sizeof(BOOTSTRAP_PROPERTY); + } else if (strncmp(p, JAVA_COMMAND_PROPERTY "=", + sizeof(JAVA_COMMAND_PROPERTY)) == 0 + or strncmp(p, JAVA_LAUNCHER_PROPERTY "=", + sizeof(JAVA_LAUNCHER_PROPERTY)) == 0) + { + // this means we're being invoked via the javac or java + // command, so the bootstrap library should be e.g. libjvm.so + bootLibraries = SO_PREFIX "jvm" SO_SUFFIX; } else if (strncmp(p, CRASHDIR_PROPERTY "=", sizeof(CRASHDIR_PROPERTY)) == 0) { @@ -3719,9 +3895,16 @@ JNI_CreateJavaVM(Machine** m, Thread** t, void* args) *RUNTIME_ARRAY_BODY(bootClasspathBuffer) = 0; } + char* bootLibrary = bootLibraries ? strdup(bootLibraries) : 0; + char* bootLibraryEnd = bootLibrary ? strchr(bootLibrary, PATH_SEPARATOR) : 0; + if(bootLibraryEnd) + *bootLibraryEnd = 0; + Finder* bf = makeFinder (s, h, RUNTIME_ARRAY_BODY(bootClasspathBuffer), bootLibrary); Finder* af = makeFinder(s, h, classpath, bootLibrary); + if(bootLibrary) + free(bootLibrary); Processor* p = makeProcessor(s, h, true); const char** properties = static_cast diff --git a/src/lzma-decode.cpp b/src/lzma-decode.cpp index 874e6f584d..d0a2c8cbbd 100644 --- a/src/lzma-decode.cpp +++ b/src/lzma-decode.cpp @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "lzma-util.h" +#include "avian/lzma-util.h" #include "C/LzmaDec.h" using namespace vm; diff --git a/src/lzma-encode.cpp b/src/lzma-encode.cpp index cc7f8ad178..e488e2441d 100644 --- a/src/lzma-encode.cpp +++ b/src/lzma-encode.cpp @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "lzma-util.h" +#include "avian/lzma-util.h" #include "C/LzmaEnc.h" using namespace vm; diff --git a/src/machine.cpp b/src/machine.cpp index b9782ac3f0..c0cdaaf925 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -8,16 +8,25 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "jnienv.h" -#include "machine.h" -#include "util.h" -#include "stream.h" -#include "constants.h" -#include "processor.h" -#include "arch.h" -#include "lzma.h" +#include "avian/jnienv.h" +#include "avian/machine.h" +#include "avian/util.h" +#include +#include "avian/constants.h" +#include "avian/processor.h" +#include "avian/arch.h" +#include "avian/lzma.h" + +#include +#include + +#if defined(PLATFORM_WINDOWS) +# define WIN32_LEAN_AND_MEAN +# include +#endif using namespace vm; +using namespace avian::util; namespace { @@ -273,6 +282,8 @@ killZombies(Thread* t, Thread* o) unsigned footprint(Thread* t) { + expect(t, t->criticalLevel == 0); + unsigned n = t->heapOffset + t->heapIndex + t->backupHeapIndex; for (Thread* c = t->child; c; c = c->peer) { @@ -305,9 +316,9 @@ bool walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, unsigned arrayElementSize, unsigned arrayLength, unsigned start) { - unsigned fixedSizeInWords = ceiling(fixedSize, BytesPerWord); + unsigned fixedSizeInWords = ceilingDivide(fixedSize, BytesPerWord); unsigned arrayElementSizeInWords - = ceiling(arrayElementSize, BytesPerWord); + = ceilingDivide(arrayElementSize, BytesPerWord); for (unsigned i = start; i < fixedSizeInWords; ++i) { if (mask[i / 32] & (static_cast(1) << (i % 32))) { @@ -472,6 +483,25 @@ referenceTargetReachable(Thread* t, Heap::Visitor* v, object* p) } } +bool +isFinalizable(Thread* t, object o) +{ + return t->m->heap->status(o) == Heap::Unreachable + and (classVmFlags + (t, static_cast(t->m->heap->follow(objectClass(t, o)))) + & HasFinalizerFlag); +} + +void +clearTargetIfFinalizable(Thread* t, object r) +{ + if (isFinalizable + (t, static_cast(t->m->heap->follow(jreferenceTarget(t, r))))) + { + jreferenceTarget(t, r) = 0; + } +} + void postVisit(Thread* t, Heap::Visitor* v) { @@ -480,16 +510,49 @@ postVisit(Thread* t, Heap::Visitor* v) assert(t, m->finalizeQueue == 0); + m->heap->postVisit(); + + for (object p = m->weakReferences; p;) { + object r = static_cast(m->heap->follow(p)); + p = jreferenceVmNext(t, r); + clearTargetIfFinalizable(t, r); + } + + if (major) { + for (object p = m->tenuredWeakReferences; p;) { + object r = static_cast(m->heap->follow(p)); + p = jreferenceVmNext(t, r); + clearTargetIfFinalizable(t, r); + } + } + + for (Reference* r = m->jniReferences; r; r = r->next) { + if (r->weak and isFinalizable + (t, static_cast(t->m->heap->follow(r->target)))) + { + r->target = 0; + } + } + object firstNewTenuredFinalizer = 0; object lastNewTenuredFinalizer = 0; - for (object* p = &(m->finalizers); *p;) { - v->visit(p); + { object unreachable = 0; + for (object* p = &(m->finalizers); *p;) { + v->visit(p); - if (m->heap->status(finalizerTarget(t, *p)) == Heap::Unreachable) { - // target is unreachable - queue it up for finalization - finalizerTargetUnreachable(t, v, p); - } else { + if (m->heap->status(finalizerTarget(t, *p)) == Heap::Unreachable) { + object finalizer = *p; + *p = finalizerNext(t, finalizer); + + finalizerNext(t, finalizer) = unreachable; + unreachable = finalizer; + } else { + p = &finalizerNext(t, *p); + } + } + + for (object* p = &(m->finalizers); *p;) { // target is reachable v->visit(&finalizerTarget(t, *p)); @@ -509,6 +572,11 @@ postVisit(Thread* t, Heap::Visitor* v) p = &finalizerNext(t, *p); } } + + for (object* p = &unreachable; *p;) { + // target is unreachable - queue it up for finalization + finalizerTargetUnreachable(t, v, p); + } } object firstNewTenuredWeakReference = 0; @@ -549,17 +617,31 @@ postVisit(Thread* t, Heap::Visitor* v) } if (major) { - for (object* p = &(m->tenuredFinalizers); *p;) { - v->visit(p); + { object unreachable = 0; + for (object* p = &(m->tenuredFinalizers); *p;) { + v->visit(p); - if (m->heap->status(finalizerTarget(t, *p)) == Heap::Unreachable) { - // target is unreachable - queue it up for finalization - finalizerTargetUnreachable(t, v, p); - } else { + if (m->heap->status(finalizerTarget(t, *p)) == Heap::Unreachable) { + object finalizer = *p; + *p = finalizerNext(t, finalizer); + + finalizerNext(t, finalizer) = unreachable; + unreachable = finalizer; + } else { + p = &finalizerNext(t, *p); + } + } + + for (object* p = &(m->tenuredFinalizers); *p;) { // target is reachable v->visit(&finalizerTarget(t, *p)); p = &finalizerNext(t, *p); } + + for (object* p = &unreachable; *p;) { + // target is unreachable - queue it up for finalization + finalizerTargetUnreachable(t, v, p); + } } for (object* p = &(m->tenuredWeakReferences); *p;) { @@ -682,7 +764,7 @@ finalizeObject(Thread* t, object o, const char* name) } unsigned -readByte(Stream& s, unsigned* value) +readByte(AbstractStream& s, unsigned* value) { if (*value == NoByte) { return s.read1(); @@ -694,8 +776,9 @@ readByte(Stream& s, unsigned* value) } object -parseUtf8NonAscii(Thread* t, Stream& s, object bytesSoFar, unsigned byteCount, - unsigned sourceIndex, unsigned byteA, unsigned byteB) +parseUtf8NonAscii(Thread* t, AbstractStream& s, object bytesSoFar, + unsigned byteCount, unsigned sourceIndex, unsigned byteA, + unsigned byteB) { PROTECT(t, bytesSoFar); @@ -747,7 +830,7 @@ parseUtf8NonAscii(Thread* t, Stream& s, object bytesSoFar, unsigned byteCount, } object -parseUtf8(Thread* t, Stream& s, unsigned length) +parseUtf8(Thread* t, AbstractStream& s, unsigned length) { object value = makeByteArray(t, length + 1); unsigned vi = 0; @@ -785,6 +868,14 @@ parseUtf8(Thread* t, Stream& s, unsigned length) return value; } +object +makeByteArray(Thread* t, Stream& s, unsigned length) +{ + object value = makeByteArray(t, length + 1); + s.read(reinterpret_cast(&byteArrayBody(t, value, 0)), length); + return value; +} + void removeByteArray(Thread* t, object o) { @@ -840,10 +931,7 @@ 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) == type(t, Machine::ByteArrayType)) { - value = internByteArray(t, value); - } + object value = internByteArray(t, makeByteArray(t, s, s.read2())); set(t, pool, SingletonBody + (i * BytesPerWord), value); if(DebugClassReader) { @@ -871,9 +959,9 @@ parsePoolEntry(Thread* t, Stream& s, uint32_t* index, object pool, unsigned i) unsigned si = s.read2() - 1; parsePoolEntry(t, s, index, pool, si); - object value = singletonObject(t, pool, si); + object value = parseUtf8(t, singletonObject(t, pool, si)); value = t->m->classpath->makeString - (t, value, 0, cast(value, BytesPerWord) - 1); + (t, value, 0, fieldAtOffset(value, BytesPerWord) - 1); value = intern(t, value); set(t, pool, SingletonBody + (i * BytesPerWord), value); @@ -1035,7 +1123,7 @@ getClassAddendum(Thread* t, object class_, object pool) if (addendum == 0) { PROTECT(t, class_); - addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, 0); + addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, 0, 0, 0); set(t, class_, ClassAddendum, addendum); } return addendum; @@ -1128,7 +1216,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) unsigned count = s.read2(); if (count) { - unsigned staticOffset = BytesPerWord * 2; + unsigned staticOffset = BytesPerWord * 3; unsigned staticCount = 0; object fieldTable = makeArray(t, count); @@ -1235,14 +1323,17 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) set(t, class_, ClassFieldTable, fieldTable); if (staticCount) { - unsigned footprint = ceiling(staticOffset - (BytesPerWord * 2), + unsigned footprint = ceilingDivide(staticOffset - (BytesPerWord * 2), BytesPerWord); object staticTable = makeSingletonOfSize(t, footprint); uint8_t* body = reinterpret_cast (&singletonBody(t, staticTable, 0)); - for (unsigned i = 0, offset = 0; i < staticCount; ++i) { + memcpy(body, &class_, BytesPerWord); + singletonMarkObject(t, staticTable, 0); + + for (unsigned i = 0, offset = BytesPerWord; i < staticCount; ++i) { unsigned size = fieldSize(t, RUNTIME_ARRAY_BODY(staticTypes)[i]); while (offset % size) { ++ offset; @@ -1303,7 +1394,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) classObjectMask(t, classSuper(t, class_))); } else { object mask = makeIntArray - (t, ceiling(classFixedSize(t, class_), 32 * BytesPerWord)); + (t, ceilingDivide(classFixedSize(t, class_), 32 * BytesPerWord)); intArrayBody(t, mask, 0) = 1; object superMask = 0; @@ -1312,7 +1403,7 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) if (superMask) { memcpy(&intArrayBody(t, mask, 0), &intArrayBody(t, superMask, 0), - ceiling(classFixedSize(t, classSuper(t, class_)), + ceilingDivide(classFixedSize(t, classSuper(t, class_)), 32 * BytesPerWord) * 4); } @@ -2212,12 +2303,20 @@ parseAttributeTable(Thread* t, Stream& s, object class_, object pool) int16_t flags = s.read2(); object reference = makeInnerClassReference - (t, inner ? singletonObject(t, pool, inner - 1) : 0, - outer ? singletonObject(t, pool, outer - 1) : 0, + (t, + inner ? referenceName(t, singletonObject(t, pool, inner - 1)) : 0, + outer ? referenceName(t, singletonObject(t, pool, outer - 1)) : 0, name ? singletonObject(t, pool, name - 1) : 0, flags); set(t, table, ArrayBody + (i * BytesPerWord), reference); + + if (0 == strcmp + (&byteArrayBody(t, className(t, class_), 0), + &byteArrayBody(t, innerClassReferenceInner(t, reference), 0))) + { + classFlags(t, class_) = flags; + } } object addendum = getClassAddendum(t, class_, pool); @@ -2232,6 +2331,20 @@ parseAttributeTable(Thread* t, Stream& s, object class_, object pool) object addendum = getClassAddendum(t, class_, pool); set(t, addendum, AddendumAnnotationTable, body); + } else if (vm::strcmp(reinterpret_cast + ("EnclosingMethod"), + &byteArrayBody(t, name, 0)) == 0) + { + int16_t enclosingClass = s.read2(); + int16_t enclosingMethod = s.read2(); + + object addendum = getClassAddendum(t, class_, pool); + + set(t, addendum, ClassAddendumEnclosingClass, + referenceName(t, singletonObject(t, pool, enclosingClass - 1))); + + set(t, addendum, ClassAddendumEnclosingMethod, enclosingMethod + ? singletonObject(t, pool, enclosingMethod - 1) : 0); } else { s.skip(length); } @@ -2248,6 +2361,11 @@ updateClassTables(Thread* t, object newClass, object oldClass) } } + object staticTable = classStaticTable(t, newClass); + if (staticTable) { + set(t, staticTable, SingletonBody, newClass); + } + if (classFlags(t, newClass) & ACC_INTERFACE) { object virtualTable = classVirtualTable(t, newClass); if (virtualTable) { @@ -2257,12 +2375,12 @@ updateClassTables(Thread* t, object newClass, object oldClass) } } } - } else { - object methodTable = classMethodTable(t, newClass); - if (methodTable) { - for (unsigned i = 0; i < arrayLength(t, methodTable); ++i) { - set(t, arrayBody(t, methodTable, i), MethodClass, newClass); - } + } + + object methodTable = classMethodTable(t, newClass); + if (methodTable) { + for (unsigned i = 0; i < arrayLength(t, methodTable); ++i) { + set(t, arrayBody(t, methodTable, i), MethodClass, newClass); } } } @@ -2303,8 +2421,6 @@ object makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec, object elementClass) { - // todo: arrays should implement Cloneable and Serializable - if (classVmFlags(t, type(t, Machine::JobjectType)) & BootstrapFlag) { PROTECT(t, loader); PROTECT(t, spec); @@ -2332,7 +2448,7 @@ makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec, spec, 0, type(t, Machine::JobjectType), - 0, + root(t, Machine::ArrayInterfaceTable), vtable, 0, 0, @@ -2394,10 +2510,13 @@ makeArrayClass(Thread* t, object loader, object spec, bool throw_, default: if (dimensions > 1) { char c = *s; - elementSpec = makeByteArray(t, 3); - byteArrayBody(t, elementSpec, 0) = '['; - byteArrayBody(t, elementSpec, 1) = c; - byteArrayBody(t, elementSpec, 2) = 0; + elementSpec = makeByteArray(t, dimensions + 1); + unsigned i; + for (i = 0; i < dimensions - 1; ++i) { + byteArrayBody(t, elementSpec, i) = '['; + } + byteArrayBody(t, elementSpec, i++) = c; + byteArrayBody(t, elementSpec, i) = 0; -- dimensions; } else { abort(t); @@ -2556,6 +2675,20 @@ nameClass(Thread* t, Machine::Type type, const char* name) set(t, arrayBody(t, t->m->types, type), ClassName, n); } +void +makeArrayInterfaceTable(Thread* t) +{ + object interfaceTable = makeArray(t, 4); + + set(t, interfaceTable, ArrayBody, type + (t, Machine::SerializableType)); + + set(t, interfaceTable, ArrayBody + (2 * BytesPerWord), + type(t, Machine::CloneableType)); + + setRoot(t, Machine::ArrayInterfaceTable, interfaceTable); +} + void boot(Thread* t) { @@ -2667,6 +2800,25 @@ boot(Thread* t) setRoot(t, Machine::StringMap, makeWeakHashMap(t, 0, 0)); + makeArrayInterfaceTable(t); + + set(t, type(t, Machine::BooleanArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::ByteArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::CharArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::ShortArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::IntArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::LongArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::FloatArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + set(t, type(t, Machine::DoubleArrayType), ClassInterfaceTable, + root(t, Machine::ArrayInterfaceTable)); + m->processor->boot(t, 0, 0); { object bootCode = makeCode(t, 0, 0, 0, 0, 0, 0, 0, 1); @@ -2690,8 +2842,6 @@ class HeapClient: public Heap::Client { virtual void visitRoots(Heap::Visitor* v) { ::visitRoots(m, v); - m->heap->postVisit(); - postVisit(m->rootThread, v); } @@ -2706,7 +2856,7 @@ class HeapClient: public Heap::Client { virtual unsigned sizeInWords(void* p) { Thread* t = m->rootThread; - object o = static_cast(m->heap->follow(mask(p))); + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); unsigned n = baseSize(t, o, static_cast (m->heap->follow(objectClass(t, o)))); @@ -2721,7 +2871,7 @@ class HeapClient: public Heap::Client { virtual unsigned copiedSizeInWords(void* p) { Thread* t = m->rootThread; - object o = static_cast(m->heap->follow(mask(p))); + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); assert(t, not objectFixed(t, o)); unsigned n = baseSize(t, o, static_cast @@ -2737,7 +2887,7 @@ class HeapClient: public Heap::Client { virtual void copy(void* srcp, void* dstp) { Thread* t = m->rootThread; - object src = static_cast(m->heap->follow(mask(srcp))); + object src = static_cast(m->heap->follow(maskAlignedPointer(srcp))); assert(t, not objectFixed(t, src)); object class_ = static_cast @@ -2758,7 +2908,7 @@ class HeapClient: public Heap::Client { } virtual void walk(void* p, Heap::Walker* w) { - object o = static_cast(m->heap->follow(mask(p))); + object o = static_cast(m->heap->follow(maskAlignedPointer(p))); ::walk(m->rootThread, w, o, 0); } @@ -2771,7 +2921,7 @@ class HeapClient: public Heap::Client { }; void -doCollect(Thread* t, Heap::CollectionType type) +doCollect(Thread* t, Heap::CollectionType type, int pendingAllocation) { expect(t, not t->m->collecting); @@ -2786,7 +2936,8 @@ doCollect(Thread* t, Heap::CollectionType type) Machine* m = t->m; m->unsafe = true; - m->heap->collect(type, footprint(m->rootThread)); + m->heap->collect(type, footprint(m->rootThread), pendingAllocation + - (t->m->heapPoolIndex * ThreadHeapSizeInWords)); m->unsafe = false; postCollect(m->rootThread); @@ -2819,7 +2970,8 @@ doCollect(Thread* t, Heap::CollectionType type) } if ((root(t, Machine::ObjectsToFinalize) or root(t, Machine::ObjectsToClean)) - and m->finalizeThread == 0) + and m->finalizeThread == 0 + and t->state != Thread::ExitState) { m->finalizeThread = m->processor->makeThread (m, root(t, Machine::FinalizerThread), m->rootThread); @@ -2946,6 +3098,14 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, populateJNITables(&javaVMVTable, &jniEnvVTable); + const char* bootstrapProperty = findProperty(this, BOOTSTRAP_PROPERTY); + const char* bootstrapPropertyDup = bootstrapProperty ? strdup(bootstrapProperty) : 0; + const char* bootstrapPropertyEnd = bootstrapPropertyDup + (bootstrapPropertyDup ? strlen(bootstrapPropertyDup) : 0); + char* codeLibraryName = (char*)bootstrapPropertyDup; + char* codeLibraryNameEnd = 0; + if (codeLibraryName && (codeLibraryNameEnd = strchr(codeLibraryName, system->pathSeparator()))) + *codeLibraryNameEnd = 0; + if (not system->success(system->make(&localThread)) or not system->success(system->make(&stateLock)) or not system->success(system->make(&heapLock)) or @@ -2953,10 +3113,25 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, not system->success(system->make(&referenceLock)) or not system->success(system->make(&shutdownLock)) or not system->success - (system->load(&libraries, findProperty(this, "avian.bootstrap")))) + (system->load(&libraries, bootstrapPropertyDup))) { system->abort(); } + + System::Library* additionalLibrary = 0; + while (codeLibraryNameEnd && codeLibraryNameEnd + 1 < bootstrapPropertyEnd) { + codeLibraryName = codeLibraryNameEnd + 1; + codeLibraryNameEnd = strchr(codeLibraryName, system->pathSeparator()); + if (codeLibraryNameEnd) + *codeLibraryNameEnd = 0; + + if (!system->success(system->load(&additionalLibrary, codeLibraryName))) + system->abort(); + libraries->setNext(additionalLibrary); + } + + if(bootstrapPropertyDup) + free((void*)bootstrapPropertyDup); } void @@ -3084,6 +3259,7 @@ Thread::init() if (image and code) { m->processor->boot(this, image, code); + makeArrayInterfaceTable(this); } else { boot(this); } @@ -3097,10 +3273,6 @@ Thread::init() setRoot(this, Machine::JNIFieldTable, makeVector(this, 0, 0)); m->localThread->set(this); - - javaThread = m->classpath->makeThread(this, 0); - - threadPeer(this, javaThread) = reinterpret_cast(this); } expect(this, m->system->success(m->system->make(&lock))); @@ -3363,7 +3535,7 @@ enter(Thread* t, Thread::State s) switch (t->state) { case Thread::ExclusiveState: { assert(t, t->m->exclusive == t); - t->m->exclusive = 0; + // exit state should also be exclusive, so don't set exclusive = 0 t->m->stateLock->notifyAll(t->systemThread); } break; @@ -3392,7 +3564,7 @@ allocate2(Thread* t, unsigned sizeInBytes, bool objectMask) { return allocate3 (t, t->m->heap, - ceiling(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords ? + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords ? Machine::FixedAllocation : Machine::MovableAllocation, sizeInBytes, objectMask); } @@ -3401,16 +3573,18 @@ object allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, unsigned sizeInBytes, bool objectMask) { + expect(t, t->criticalLevel == 0); + if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) { - expect(t, t->backupHeapIndex + ceiling(sizeInBytes, BytesPerWord) + expect(t, t->backupHeapIndex + ceilingDivide(sizeInBytes, BytesPerWord) <= ThreadBackupHeapSizeInWords); object o = reinterpret_cast(t->backupHeap + t->backupHeapIndex); - t->backupHeapIndex += ceiling(sizeInBytes, BytesPerWord); - cast(o, 0) = 0; + t->backupHeapIndex += ceilingDivide(sizeInBytes, BytesPerWord); + fieldAtOffset(o, 0) = 0; return o; } else if (UNLIKELY(t->flags & Thread::TracingFlag)) { - expect(t, t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + expect(t, t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) <= ThreadHeapSizeInWords); return allocateSmall(t, sizeInBytes); } @@ -3430,7 +3604,7 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, do { switch (type) { case Machine::MovableAllocation: - if (t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + if (t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords) { t->heap = 0; @@ -3462,44 +3636,46 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, break; } - if (t->heap == 0) { + int pendingAllocation = t->m->heap->fixedFootprint + (ceilingDivide(sizeInBytes, BytesPerWord), objectMask); + + if (t->heap == 0 or t->m->heap->limitExceeded(pendingAllocation)) { // fprintf(stderr, "gc"); // vmPrintTrace(t); - collect(t, Heap::MinorCollection); + collect(t, Heap::MinorCollection, pendingAllocation); } - if (t->m->heap->limitExceeded()) { + if (t->m->heap->limitExceeded(pendingAllocation)) { throw_(t, root(t, Machine::OutOfMemoryError)); } } while (type == Machine::MovableAllocation - and t->heapIndex + ceiling(sizeInBytes, BytesPerWord) + and t->heapIndex + ceilingDivide(sizeInBytes, BytesPerWord) > ThreadHeapSizeInWords); - + switch (type) { case Machine::MovableAllocation: { return allocateSmall(t, sizeInBytes); } case Machine::FixedAllocation: { - unsigned total; object o = static_cast (t->m->heap->allocateFixed - (allocator, ceiling(sizeInBytes, BytesPerWord), objectMask, &total)); + (allocator, ceilingDivide(sizeInBytes, BytesPerWord), objectMask)); memset(o, 0, sizeInBytes); alias(o, 0) = FixedMark; - - t->m->fixedFootprint += total; - + + t->m->fixedFootprint += t->m->heap->fixedFootprint + (ceilingDivide(sizeInBytes, BytesPerWord), objectMask); + return o; } case Machine::ImmortalAllocation: { - unsigned total; object o = static_cast (t->m->heap->allocateImmortalFixed - (allocator, ceiling(sizeInBytes, BytesPerWord), objectMask, &total)); + (allocator, ceilingDivide(sizeInBytes, BytesPerWord), objectMask)); memset(o, 0, sizeInBytes); @@ -3512,6 +3688,27 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, } } +void +collect(Thread* t, Heap::CollectionType type, int pendingAllocation) +{ + ENTER(t, Thread::ExclusiveState); + + unsigned pending = pendingAllocation + - (t->m->heapPoolIndex * ThreadHeapSizeInWords); + + if (t->m->heap->limitExceeded(pending)) { + type = Heap::MajorCollection; + } + + doCollect(t, type, pendingAllocation); + + if (t->m->heap->limitExceeded(pending)) { + // try once more, giving the heap a chance to squeeze everything + // into the smallest possible space: + doCollect(t, Heap::MajorCollection, pendingAllocation); + } +} + object makeNewGeneral(Thread* t, object class_) { @@ -3869,7 +4066,7 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size, Client(Thread* t): t(t) { } virtual void NO_RETURN handleError() { - vm::abort(t); + abort(t); } private: @@ -3983,6 +4180,17 @@ parseClass(Thread* t, object loader, const uint8_t* data, unsigned size, return real; } +uint64_t +runParseClass(Thread* t, uintptr_t* arguments) +{ + object loader = reinterpret_cast(arguments[0]); + System::Region* region = reinterpret_cast(arguments[1]); + Machine::Type throwType = static_cast(arguments[2]); + + return reinterpret_cast + (parseClass(t, loader, region->start(), region->length(), throwType)); +} + object resolveSystemClass(Thread* t, object loader, object spec, bool throw_, Machine::Type throwType) @@ -4028,9 +4236,24 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_, { THREAD_RESOURCE(t, System::Region*, region, region->dispose()); + uintptr_t arguments[] = { reinterpret_cast(loader), + reinterpret_cast(region), + static_cast(throwType) }; + // parse class file - class_ = parseClass - (t, loader, region->start(), region->length(), throwType); + class_ = reinterpret_cast + (runRaw(t, runParseClass, arguments)); + + if (UNLIKELY(t->exception)) { + if (throw_) { + object e = t->exception; + t->exception = 0; + vm::throw_(t, e); + } else { + t->exception = 0; + return 0; + } + } } if (Verbose) { @@ -4068,6 +4291,8 @@ resolveSystemClass(Thread* t, object loader, object spec, bool throw_, if (class_) { hashMapInsert(t, classLoaderMap(t, loader), spec, class_, byteArrayHash); + + t->m->classpath->updatePackageMap(t, class_); } else if (throw_) { throwNew(t, throwType, "%s", &byteArrayBody(t, spec, 0)); } @@ -4529,38 +4754,6 @@ intern(Thread* t, object s) } } -void -collect(Thread* t, Heap::CollectionType type) -{ - ENTER(t, Thread::ExclusiveState); - - if (t->m->heap->limitExceeded()) { - type = Heap::MajorCollection; - } - - doCollect(t, type); - - if (t->m->heap->limitExceeded()) { - // try once more, giving the heap a chance to squeeze everything - // into the smallest possible space: - doCollect(t, Heap::MajorCollection); - } - -#ifdef AVIAN_HEAPDUMP - if ((not t->m->dumpedHeapOnOOM) and t->m->heap->limitExceeded()) { - t->m->dumpedHeapOnOOM = true; - const char* path = findProperty(t, "avian.heap.dump"); - if (path) { - FILE* out = vm::fopen(path, "wb"); - if (out) { - dumpHeap(t, out); - fclose(out); - } - } - } -#endif//AVIAN_HEAPDUMP -} - void walk(Thread* t, Heap::Walker* w, object o, unsigned start) { @@ -4575,7 +4768,7 @@ walk(Thread* t, Heap::Walker* w, object o, unsigned start) unsigned arrayElementSize = classArrayElementSize(t, class_); unsigned arrayLength = (arrayElementSize ? - cast(o, fixedSize - BytesPerWord) : 0); + fieldAtOffset(o, fixedSize - BytesPerWord) : 0); THREAD_RUNTIME_ARRAY(t, uint32_t, mask, intArrayLength(t, objectMask)); memcpy(RUNTIME_ARRAY_BODY(mask), &intArrayBody(t, objectMask, 0), @@ -4636,6 +4829,30 @@ visitRoots(Machine* m, Heap::Visitor* v) } } +void +logTrace(FILE* f, const char* fmt, ...) +{ + va_list a; + va_start(a, fmt); +#ifdef PLATFORM_WINDOWS + const unsigned length = _vscprintf(fmt, a); +#else + const unsigned length = vsnprintf(0, 0, fmt, a); +#endif + va_end(a); + + RUNTIME_ARRAY(char, buffer, length + 1); + va_start(a, fmt); + vsnprintf(RUNTIME_ARRAY_BODY(buffer), length + 1, fmt, a); + va_end(a); + RUNTIME_ARRAY_BODY(buffer)[length] = 0; + + ::fprintf(f, "%s", RUNTIME_ARRAY_BODY(buffer)); +#ifdef PLATFORM_WINDOWS + ::OutputDebugStringA(RUNTIME_ARRAY_BODY(buffer)); +#endif +} + void printTrace(Thread* t, object exception) { @@ -4645,19 +4862,19 @@ printTrace(Thread* t, object exception) for (object e = exception; e; e = throwableCause(t, e)) { if (e != exception) { - fprintf(errorLog(t), "caused by: "); + logTrace(errorLog(t), "caused by: "); } - fprintf(errorLog(t), "%s", &byteArrayBody + logTrace(errorLog(t), "%s", &byteArrayBody (t, className(t, objectClass(t, e)), 0)); - + if (throwableMessage(t, e)) { object m = throwableMessage(t, e); THREAD_RUNTIME_ARRAY(t, char, message, stringLength(t, m) + 1); stringChars(t, m, RUNTIME_ARRAY_BODY(message)); - fprintf(errorLog(t), ": %s\n", RUNTIME_ARRAY_BODY(message)); + logTrace(errorLog(t), ": %s\n", RUNTIME_ARRAY_BODY(message)); } else { - fprintf(errorLog(t), "\n"); + logTrace(errorLog(t), "\n"); } object trace = throwableTrace(t, e); @@ -4671,17 +4888,17 @@ printTrace(Thread* t, object exception) int line = t->m->processor->lineNumber (t, traceElementMethod(t, e), traceElementIp(t, e)); - fprintf(errorLog(t), " at %s.%s ", class_, method); + logTrace(errorLog(t), " at %s.%s ", class_, method); switch (line) { case NativeLine: - fprintf(errorLog(t), "(native)\n"); + logTrace(errorLog(t), "(native)\n"); break; case UnknownLine: - fprintf(errorLog(t), "(unknown line)\n"); + logTrace(errorLog(t), "(unknown line)\n"); break; default: - fprintf(errorLog(t), "(line %d)\n", line); + logTrace(errorLog(t), "(line %d)\n", line); } } } @@ -4691,7 +4908,7 @@ printTrace(Thread* t, object exception) } } - fflush(errorLog(t)); + ::fflush(errorLog(t)); } object @@ -4704,11 +4921,11 @@ makeTrace(Thread* t, Processor::StackWalker* walker) virtual bool visit(Processor::StackWalker* walker) { if (trace == 0) { trace = makeObjectArray(t, walker->count()); - vm_assert(t, trace); + assert(t, trace); } object e = makeTraceElement(t, walker->method(), walker->ip()); - vm_assert(t, index < objectArrayLength(t, trace)); + assert(t, index < objectArrayLength(t, trace)); set(t, trace, ArrayBody + (index * BytesPerWord), e); ++ index; return true; @@ -4808,15 +5025,83 @@ parseUtf8(Thread* t, const char* data, unsigned length) } object -getCaller(Thread* t, unsigned target) +parseUtf8(Thread* t, object array) +{ + for (unsigned i = 0; i < byteArrayLength(t, array) - 1; ++i) { + if (byteArrayBody(t, array, i) & 0x80) { + goto slow_path; + } + } + + return array; + + slow_path: + class Client: public Stream::Client { + public: + Client(Thread* t): t(t) { } + + virtual void handleError() { + // vm::abort(t); + } + + private: + Thread* t; + } client(t); + + class MyStream: public AbstractStream { + public: + class MyProtector: public Thread::Protector { + public: + MyProtector(Thread* t, MyStream* s): + Protector(t), s(s) + { } + + virtual void visit(Heap::Visitor* v) { + v->visit(&(s->array)); + } + + MyStream* s; + }; + + MyStream(Thread* t, Client* client, object array): + AbstractStream(client, byteArrayLength(t, array) - 1), + array(array), + protector(t, this) + { } + + virtual void copy(uint8_t* dst, unsigned offset, unsigned size) { + memcpy(dst, &byteArrayBody(protector.t, array, offset), size); + } + + object array; + MyProtector protector; + } s(t, &client, array); + + return ::parseUtf8(t, s, byteArrayLength(t, array) - 1); +} + +object +getCaller(Thread* t, unsigned target, bool skipMethodInvoke) { class Visitor: public Processor::StackVisitor { public: - Visitor(Thread* t, unsigned target): - t(t), method(0), count(0), target(target) + Visitor(Thread* t, unsigned target, bool skipMethodInvoke): + t(t), method(0), count(0), target(target), + skipMethodInvoke(skipMethodInvoke) { } virtual bool visit(Processor::StackWalker* walker) { + if (skipMethodInvoke + and methodClass + (t, walker->method()) == type(t, Machine::JmethodType) + and strcmp + (&byteArrayBody(t, methodName(t, walker->method()), 0), + reinterpret_cast("invoke")) + == 0) + { + return true; + } + if (count == target) { method = walker->method(); return false; @@ -4830,7 +5115,8 @@ getCaller(Thread* t, unsigned target) object method; unsigned count; unsigned target; - } v(t, target); + bool skipMethodInvoke; + } v(t, target, skipMethodInvoke); t->m->processor->walkStack(t, &v); @@ -4890,7 +5176,7 @@ populateMultiArray(Thread* t, object array, int32_t* counts, for (int32_t i = 0; i < counts[index]; ++i) { object a = makeArray - (t, ceiling + (t, ceilingDivide (counts[index + 1] * classArrayElementSize(t, class_), BytesPerWord)); arrayLength(t, a) = counts[index + 1]; setObjectClass(t, a, class_); @@ -4910,11 +5196,11 @@ noop() // for debugging JNIEXPORT void -vmPrintTrace(Thread* t) +vmfPrintTrace(Thread* t, FILE* out) { class Visitor: public Processor::StackVisitor { public: - Visitor(Thread* t): t(t) { } + Visitor(Thread* t, FILE* out): t(t), out(out) { } virtual bool visit(Processor::StackWalker* walker) { const int8_t* class_ = &byteArrayBody @@ -4924,30 +5210,37 @@ vmPrintTrace(Thread* t) int line = t->m->processor->lineNumber (t, walker->method(), walker->ip()); - fprintf(stderr, " at %s.%s ", class_, method); + fprintf(out, " at %s.%s ", class_, method); switch (line) { case NativeLine: - fprintf(stderr, "(native)\n"); + fprintf(out, "(native)\n"); break; case UnknownLine: - fprintf(stderr, "(unknown line)\n"); + fprintf(out, "(unknown line)\n"); break; default: - fprintf(stderr, "(line %d)\n", line); + fprintf(out, "(line %d)\n", line); } return true; } Thread* t; - } v(t); + FILE* out; + } v(t, out); - fprintf(stderr, "debug trace for thread %p\n", t); + fprintf(out, "debug trace for thread %p\n", t); t->m->processor->walkStack(t, &v); - fflush(stderr); + fflush(out); +} + +JNIEXPORT void +vmPrintTrace(Thread* t) +{ + vmfPrintTrace(t, stderr); } // also for debugging diff --git a/src/main.cpp b/src/main.cpp index fe2793c10d..8876011e79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,8 +13,10 @@ #include "string.h" #include "jni.h" -#include "system.h" -#include "finder.h" +#include +#include "avian/finder.h" + +#include #if (defined __MINGW32__) || (defined _MSC_VER) # define PATH_SEPARATOR ';' @@ -29,28 +31,6 @@ # define and && # define xor ^ -template -class RuntimeArray { - public: - RuntimeArray(unsigned size): - body(static_cast(malloc(size * sizeof(T)))) - { } - - ~RuntimeArray() { - free(body); - } - - T* body; -}; - -# define RUNTIME_ARRAY(type, name, size) RuntimeArray name(size); -# define RUNTIME_ARRAY_BODY(name) name.body - -#else // not _MSC_VER - -# define RUNTIME_ARRAY(type, name, size) type name[size]; -# define RUNTIME_ARRAY_BODY(name) name - #endif // not _MSC_VER #ifdef BOOT_LIBRARY @@ -279,7 +259,10 @@ main(int ac, const char** av) JNI_CreateJavaVM(&vm, &env, &vmArgs); JNIEnv* e = static_cast(env); - jclass c = e->FindClass(class_); + jclass c = 0; + if (not e->ExceptionCheck()) { + c = e->FindClass(class_); + } if (jar) { free(const_cast(class_)); diff --git a/src/openjdk/my_java_props_macosx.c b/src/openjdk/my_java_props_macosx.c index 8a1850010f..19de63cb35 100644 --- a/src/openjdk/my_java_props_macosx.c +++ b/src/openjdk/my_java_props_macosx.c @@ -3,7 +3,7 @@ PreferredToolkit getPreferredToolkit() { - return unset; + return XToolkit; } void diff --git a/src/openjdk/stubs.cpp b/src/openjdk/stubs.cpp new file mode 100644 index 0000000000..62b3f94649 --- /dev/null +++ b/src/openjdk/stubs.cpp @@ -0,0 +1,20 @@ +#include "avian/machine.h" + +using namespace vm; + +extern "C" JNIEXPORT jint JNICALL +net_JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} + +extern "C" JNIEXPORT jint JNICALL +management_JNI_OnLoad(JavaVM*, void*) +{ + return 0; +} + +extern "C" char* findJavaTZ_md(const char*, const char*) +{ + return 0; +} diff --git a/src/powerpc.S b/src/powerpc.S index ec685da04b..1d85861e0f 100644 --- a/src/powerpc.S +++ b/src/powerpc.S @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" +#include "avian/types.h" .text diff --git a/src/powerpc.cpp b/src/powerpc.cpp deleted file mode 100644 index 6a329a2f6e..0000000000 --- a/src/powerpc.cpp +++ /dev/null @@ -1,2880 +0,0 @@ -/* Copyright (c) 2009-2012, 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 "assembler.h" -#include "vector.h" - -#define CAST1(x) reinterpret_cast(x) -#define CAST2(x) reinterpret_cast(x) -#define CAST3(x) reinterpret_cast(x) -#define CAST_BRANCH(x) reinterpret_cast(x) - -using namespace vm; - -namespace { - -namespace isa { -// INSTRUCTION FORMATS -inline int D(int op, int rt, int ra, int d) { return op<<26|rt<<21|ra<<16|(d & 0xFFFF); } -inline int DS(int op, int rt, int ra, int ds, int xo) { return op<<26|rt<<21|ra<<16|ds<<2|xo; } -inline int I(int op, int li, int aa, int lk) { return op<<26|(li & 0x3FFFFFC)|aa<<1|lk; } -inline int B(int op, int bo, int bi, int bd, int aa, int lk) { return op<<26|bo<<21|bi<<16|(bd & 0xFFFC)|aa<<1|lk; } -inline int SC(int op, int lev) { return op<<26|lev<<5|2; } -inline int X(int op, int rt, int ra, int rb, int xo, int rc) { return op<<26|rt<<21|ra<<16|rb<<11|xo<<1|rc; } -inline int XL(int op, int bt, int ba, int bb, int xo, int lk) { return op<<26|bt<<21|ba<<16|bb<<11|xo<<1|lk; } -inline int XFX(int op, int rt, int spr, int xo) { return op<<26|rt<<21|((spr >> 5) | ((spr << 5) & 0x3E0))<<11|xo<<1; } -inline int XFL(int op, int flm, int frb, int xo, int rc) { return op<<26|flm<<17|frb<<11|xo<<1|rc; } -inline int XS(int op, int rs, int ra, int sh, int xo, int sh2, int rc) { return op<<26|rs<<21|ra<<16|sh<<11|xo<<2|sh2<<1|rc; } -inline int XO(int op, int rt, int ra, int rb, int oe, int xo, int rc) { return op<<26|rt<<21|ra<<16|rb<<11|oe<<10|xo<<1|rc; } -inline int A(int op, int frt, int fra, int frb, int frc, int xo, int rc) { return op<<26|frt<<21|fra<<16|frb<<11|frc<<6|xo<<1|rc; } -inline int M(int op, int rs, int ra, int rb, int mb, int me, int rc) { return op<<26|rs<<21|ra<<16|rb<<11|mb<<6|me<<1|rc; } -inline int MD(int op, int rs, int ra, int sh, int mb, int xo, int sh2, int rc) { return op<<26|rs<<21|ra<<16|sh<<11|mb<<5|xo<<2|sh2<<1|rc; } -inline int MDS(int op, int rs, int ra, int rb, int mb, int xo, int rc) { return op<<26|rs<<21|ra<<16|rb<<11|mb<<5|xo<<1|rc; } -// INSTRUCTIONS -inline int lbz(int rt, int ra, int i) { return D(34, rt, ra, i); } -inline int lbzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 87, 0); } -inline int lha(int rt, int ra, int i) { return D(42, rt, ra, i); } -inline int lhax(int rt, int ra, int rb) { return X(31, rt, ra, rb, 343, 0); } -inline int lhz(int rt, int ra, int i) { return D(40, rt, ra, i); } -inline int lhzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 279, 0); } -inline int lwz(int rt, int ra, int i) { return D(32, rt, ra, i); } -inline int lwzx(int rt, int ra, int rb) { return X(31, rt, ra, rb, 23, 0); } -inline int stb(int rs, int ra, int i) { return D(38, rs, ra, i); } -inline int stbx(int rs, int ra, int rb) { return X(31, rs, ra, rb, 215, 0); } -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); } -inline int adde(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 138, 0); } -inline int addi(int rt, int ra, int i) { return D(14, rt, ra, i); } -inline int addic(int rt, int ra, int i) { return D(12, rt, ra, i); } -inline int addis(int rt, int ra, int i) { return D(15, rt, ra, i); } -inline int subf(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 40, 0); } -inline int subfc(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 8, 0); } -inline int subfe(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 136, 0); } -inline int subfic(int rt, int ra, int i) { return D(8, rt, ra, i); } -inline int subfze(int rt, int ra) { return XO(31, rt, ra, 0, 0, 200, 0); } -inline int mullw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 235, 0); } -inline int mulhw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 75, 0); } -inline int mulhwu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 11, 0); } -inline int mulli(int rt, int ra, int i) { return D(7, rt, ra, i); } -inline int divw(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 491, 0); } -inline int divwu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 459, 0); } -inline int divd(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 489, 0); } -inline int divdu(int rt, int ra, int rb) { return XO(31, rt, ra, rb, 0, 457, 0); } -inline int neg(int rt, int ra) { return XO(31, rt, ra, 0, 0, 104, 0); } -inline int and_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 28, 0); } -inline int andi(int rt, int ra, int i) { return D(28, ra, rt, i); } -inline int andis(int rt, int ra, int i) { return D(29, ra, rt, i); } -inline int or_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 444, 0); } -inline int ori(int rt, int ra, int i) { return D(24, rt, ra, i); } -inline int xor_(int rt, int ra, int rb) { return X(31, ra, rt, rb, 316, 0); } -inline int oris(int rt, int ra, int i) { return D(25, rt, ra, i); } -inline int xori(int rt, int ra, int i) { return D(26, rt, ra, i); } -inline int xoris(int rt, int ra, int i) { return D(27, rt, ra, i); } -inline int rlwinm(int rt, int ra, int i, int mb, int me) { return M(21, ra, rt, i, mb, me, 0); } -inline int rlwimi(int rt, int ra, int i, int mb, int me) { return M(20, ra, rt, i, mb, me, 0); } -inline int slw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 24, 0); } -inline int sld(int rt, int ra, int rb) { return X(31, ra, rt, rb, 27, 0); } -inline int srw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 536, 0); } -inline int sraw(int rt, int ra, int sh) { return X(31, ra, rt, sh, 792, 0); } -inline int srawi(int rt, int ra, int sh) { return X(31, ra, rt, sh, 824, 0); } -inline int extsb(int rt, int rs) { return X(31, rs, rt, 0, 954, 0); } -inline int extsh(int rt, int rs) { return X(31, rs, rt, 0, 922, 0); } -inline int mfspr(int rt, int spr) { return XFX(31, rt, spr, 339); } -inline int mtspr(int spr, int rs) { return XFX(31, rs, spr, 467); } -inline int b(int i) { return I(18, i, 0, 0); } -inline int bl(int i) { return I(18, i, 0, 1); } -inline int bcctr(int bo, int bi, int lk) { return XL(19, bo, bi, 0, 528, lk); } -inline int bclr(int bo, int bi, int lk) { return XL(19, bo, bi, 0, 16, lk); } -inline int bc(int bo, int bi, int bd, int lk) { return B(16, bo, bi, bd, 0, lk); } -inline int cmp(int bf, int ra, int rb) { return X(31, bf << 2, ra, rb, 0, 0); } -inline int cmpl(int bf, int ra, int rb) { return X(31, bf << 2, ra, rb, 32, 0); } -inline int cmpi(int bf, int ra, int i) { return D(11, bf << 2, ra, i); } -inline int cmpli(int bf, int ra, int i) { return D(10, bf << 2, ra, i); } -inline int sync(int L) { return X(31, L, 0, 0, 598, 0); } -// PSEUDO-INSTRUCTIONS -inline int li(int rt, int i) { return addi(rt, 0, i); } -inline int lis(int rt, int i) { return addis(rt, 0, i); } -inline int slwi(int rt, int ra, int i) { return rlwinm(rt, ra, i, 0, 31-i); } -inline int srwi(int rt, int ra, int i) { return rlwinm(rt, ra, 32-i, i, 31); } -inline int sub(int rt, int ra, int rb) { return subf(rt, rb, ra); } -inline int subc(int rt, int ra, int rb) { return subfc(rt, rb, ra); } -inline int subi(int rt, int ra, int i) { return addi(rt, ra, -i); } -inline int subis(int rt, int ra, int i) { return addis(rt, ra, -i); } -inline int mr(int rt, int ra) { return or_(rt, ra, ra); } -inline int mflr(int rx) { return mfspr(rx, 8); } -inline int mtlr(int rx) { return mtspr(8, rx); } -inline int mtctr(int rd) { return mtspr(9, rd); } -inline int bctr() { return bcctr(20, 0, 0); } -inline int bctrl() { return bcctr(20, 0, 1); } -inline int blr() { return bclr(20, 0, 0); } -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 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); } -inline int cmpwi(int ra, int i) { return cmpi(0, ra, i); } -inline int cmplwi(int ra, int i) { return cmpli(0, ra, i); } -inline int trap() { return 0x7fe00008; } // todo: macro-ify -} - -const int64_t MASK_LO32 = 0x0ffffffff; -const int MASK_LO16 = 0x0ffff; -const int MASK_LO8 = 0x0ff; -inline int lo32(int64_t i) { return (int)(i & MASK_LO32); } -inline int hi32(int64_t i) { return lo32(i >> 32); } -inline int lo16(int64_t i) { return (int)(i & MASK_LO16); } -inline int hi16(int64_t i) { return lo16(i >> 16); } -inline int lo8(int64_t i) { return (int)(i & MASK_LO8); } -inline int hi8(int64_t i) { return lo8(i >> 8); } - -inline int ha16(int32_t i) { - return ((i >> 16) + ((i & 0x8000) ? 1 : 0)) & 0xffff; -} - -inline int unha16(int32_t high, int32_t low) { - return ((high - ((low & 0x8000) ? 1 : 0)) << 16) | low; -} - -inline bool -isInt16(target_intptr_t v) -{ - return v == static_cast(v); -} - -inline int -carry16(target_intptr_t v) -{ - return static_cast(v) < 0 ? 1 : 0; -} - -#ifdef __APPLE__ -const unsigned FrameFooterSize = 6; -const unsigned ReturnAddressOffset = 2; -const unsigned AlignArguments = false; -#else -const unsigned FrameFooterSize = 2; -const unsigned ReturnAddressOffset = 1; -const unsigned AlignArguments = true; -#endif - -const unsigned StackAlignmentInBytes = 16; -const unsigned StackAlignmentInWords -= StackAlignmentInBytes / TargetBytesPerWord; - -const int StackRegister = 1; -const int ThreadRegister = 13; - -const bool DebugJumps = false; - -class Context; -class MyBlock; -class JumpOffset; -class JumpEvent; - -void -resolve(MyBlock*); - -unsigned -padding(MyBlock*, unsigned); - -class MyBlock: public Assembler::Block { - public: - MyBlock(Context* context, unsigned offset): - context(context), next(0), jumpOffsetHead(0), jumpOffsetTail(0), - lastJumpOffsetTail(0), jumpEventHead(0), jumpEventTail(0), - lastEventOffset(0), offset(offset), start(~0), size(0), resolved(false) - { } - - virtual unsigned resolve(unsigned start, Assembler::Block* next) { - this->start = start; - this->next = static_cast(next); - - ::resolve(this); - - this->resolved = true; - - return start + size + padding(this, size); - } - - Context* context; - MyBlock* next; - JumpOffset* jumpOffsetHead; - JumpOffset* jumpOffsetTail; - JumpOffset* lastJumpOffsetTail; - JumpEvent* jumpEventHead; - JumpEvent* jumpEventTail; - unsigned lastEventOffset; - unsigned offset; - unsigned start; - unsigned size; - bool resolved; -}; - -class Task; -class ConstantPoolEntry; - -class Context { - public: - Context(System* s, Allocator* a, Zone* zone): - s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), - firstBlock(new(zone) MyBlock(this, 0)), - lastBlock(firstBlock), jumpOffsetHead(0), jumpOffsetTail(0), - constantPool(0), constantPoolCount(0) - { } - - System* s; - Zone* zone; - Assembler::Client* client; - Vector code; - Task* tasks; - uint8_t* result; - MyBlock* firstBlock; - MyBlock* lastBlock; - JumpOffset* jumpOffsetHead; - JumpOffset* jumpOffsetTail; - ConstantPoolEntry* constantPool; - unsigned constantPoolCount; -}; - -class Task { - public: - Task(Task* next): next(next) { } - - virtual void run(Context* c) = 0; - - Task* next; -}; - -typedef void (*OperationType)(Context*); - -typedef void (*UnaryOperationType)(Context*, unsigned, Assembler::Operand*); - -typedef void (*BinaryOperationType) -(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*); - -typedef void (*TernaryOperationType) -(Context*, unsigned, Assembler::Operand*, Assembler::Operand*, - Assembler::Operand*); - -typedef void (*BranchOperationType) -(Context*, TernaryOperation, unsigned, Assembler::Operand*, - Assembler::Operand*, Assembler::Operand*); - -class ArchitectureContext { - public: - ArchitectureContext(System* s): s(s) { } - - System* s; - OperationType operations[OperationCount]; - UnaryOperationType unaryOperations[UnaryOperationCount - * OperandTypeCount]; - BinaryOperationType binaryOperations - [BinaryOperationCount * OperandTypeCount * OperandTypeCount]; - TernaryOperationType ternaryOperations - [NonBranchTernaryOperationCount * OperandTypeCount]; - BranchOperationType branchOperations - [BranchOperationCount * OperandTypeCount * OperandTypeCount]; -}; - -inline void NO_RETURN -abort(Context* c) -{ - abort(c->s); -} - -inline void NO_RETURN -abort(ArchitectureContext* c) -{ - abort(c->s); -} - -#ifndef NDEBUG -inline void -assert(Context* c, bool v) -{ - assert(c->s, v); -} - -inline void -assert(ArchitectureContext* c, bool v) -{ - assert(c->s, v); -} -#endif // not NDEBUG - -inline void -expect(Context* c, bool v) -{ - expect(c->s, v); -} - -class Offset: public Promise { - public: - Offset(Context* c, MyBlock* block, unsigned offset): - c(c), block(block), offset(offset) - { } - - virtual bool resolved() { - return block->resolved; - } - - virtual int64_t value() { - assert(c, resolved()); - - unsigned o = offset - block->offset; - return block->start + padding(block, o) + o; - } - - Context* c; - MyBlock* block; - unsigned offset; -}; - -Promise* -offset(Context* c) -{ - return new(c->zone) Offset(c, c->lastBlock, c->code.length()); -} - -bool -bounded(int right, int left, int32_t v) -{ - return ((v << left) >> left) == v and ((v >> right) << right) == v; -} - -void* -updateOffset(System* s, uint8_t* instruction, bool conditional, int64_t value, - void* jumpAddress) -{ - int32_t v = reinterpret_cast(value) - instruction; - - int32_t mask; - if (conditional) { - if (not bounded(2, 16, v)) { - *static_cast(jumpAddress) = isa::b(0); - updateOffset(s, static_cast(jumpAddress), false, value, 0); - - v = static_cast(jumpAddress) - instruction; - - expect(s, bounded(2, 16, v)); - } - mask = 0xFFFC; - } else { - expect(s, bounded(2, 6, v)); - mask = 0x3FFFFFC; - } - - int32_t* p = reinterpret_cast(instruction); - *p = targetV4((v & mask) | ((~mask) & targetV4(*p))); - - return instruction + 4; -} - -class OffsetListener: public Promise::Listener { - public: - OffsetListener(System* s, uint8_t* instruction, bool conditional, - void* jumpAddress): - s(s), - instruction(instruction), - jumpAddress(jumpAddress), - conditional(conditional) - { } - - virtual bool resolve(int64_t value, void** location) { - void* p = updateOffset(s, instruction, conditional, value, jumpAddress); - if (location) *location = p; - return false; - } - - System* s; - uint8_t* instruction; - void* jumpAddress; - bool conditional; -}; - -class OffsetTask: public Task { - public: - OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, - bool conditional): - Task(next), - promise(promise), - instructionOffset(instructionOffset), - jumpAddress(0), - conditional(conditional) - { } - - virtual void run(Context* c) { - if (promise->resolved()) { - updateOffset - (c->s, c->result + instructionOffset->value(), conditional, - promise->value(), jumpAddress); - } else { - new (promise->listen(sizeof(OffsetListener))) - OffsetListener(c->s, c->result + instructionOffset->value(), - conditional, jumpAddress); - } - } - - Promise* promise; - Promise* instructionOffset; - void* jumpAddress; - bool conditional; -}; - -class JumpOffset { - public: - JumpOffset(MyBlock* block, OffsetTask* task, unsigned offset): - block(block), task(task), next(0), offset(offset) - { } - - MyBlock* block; - OffsetTask* task; - JumpOffset* next; - unsigned offset; -}; - -class JumpEvent { - public: - JumpEvent(JumpOffset* jumpOffsetHead, JumpOffset* jumpOffsetTail, - unsigned offset): - jumpOffsetHead(jumpOffsetHead), jumpOffsetTail(jumpOffsetTail), next(0), - offset(offset) - { } - - JumpOffset* jumpOffsetHead; - JumpOffset* jumpOffsetTail; - JumpEvent* next; - unsigned offset; -}; - -void -appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, - bool conditional) -{ - OffsetTask* task = new(c->zone) OffsetTask(c->tasks, promise, instructionOffset, conditional); - - c->tasks = task; - - if (conditional) { - JumpOffset* offset = - new(c->zone) JumpOffset(c->lastBlock, task, c->code.length() - c->lastBlock->offset); - - if (c->lastBlock->jumpOffsetTail) { - c->lastBlock->jumpOffsetTail->next = offset; - } else { - c->lastBlock->jumpOffsetHead = offset; - } - c->lastBlock->jumpOffsetTail = offset; - } -} - -void -appendJumpEvent(Context* c, MyBlock* b, unsigned offset, JumpOffset* head, - JumpOffset* tail) -{ - JumpEvent* e = new(c->zone) JumpEvent - (head, tail, offset); - - if (b->jumpEventTail) { - b->jumpEventTail->next = e; - } else { - b->jumpEventHead = e; - } - b->jumpEventTail = e; -} - -bool -needJump(MyBlock* b) -{ - return b->next or (not bounded(2, 16, b->size)); -} - -unsigned -padding(MyBlock* b, unsigned offset) -{ - unsigned total = 0; - for (JumpEvent* e = b->jumpEventHead; e; e = e->next) { - if (e->offset <= offset) { - for (JumpOffset* o = e->jumpOffsetHead; o; o = o->next) { - total += TargetBytesPerWord; - } - - if (needJump(b)) { - total += TargetBytesPerWord; - } - } else { - break; - } - } - - return total; -} - -void -resolve(MyBlock* b) -{ - Context* c = b->context; - - for (JumpEvent** e = &(b->jumpEventHead); *e;) { - for (JumpOffset** o = &((*e)->jumpOffsetHead); *o;) { - if ((*o)->task->promise->resolved() - and (*o)->task->instructionOffset->resolved()) - { - int32_t v = reinterpret_cast((*o)->task->promise->value()) - - (c->result + (*o)->task->instructionOffset->value()); - - if (bounded(2, 16, v)) { - // this conditional jump needs no indirection -- a direct - // jump will suffice - *o = (*o)->next; - continue; - } - } - - o = &((*o)->next); - } - - if ((*e)->jumpOffsetHead == 0) { - *e = (*e)->next; - } else { - e = &((*e)->next); - } - } - - if (b->jumpOffsetHead) { - if (c->jumpOffsetTail) { - c->jumpOffsetTail->next = b->jumpOffsetHead; - } else { - c->jumpOffsetHead = b->jumpOffsetHead; - } - c->jumpOffsetTail = b->jumpOffsetTail; - } - - if (c->jumpOffsetHead) { - bool append; - if (b->next == 0 or b->next->jumpEventHead) { - append = true; - } else { - int32_t v = (b->start + b->size + b->next->size + TargetBytesPerWord) - - (c->jumpOffsetHead->offset + c->jumpOffsetHead->block->start); - - append = not bounded(2, 16, v); - - if (DebugJumps) { - fprintf(stderr, - "current %p %d %d next %p %d %d\n", - b, b->start, b->size, b->next, b->start + b->size, - b->next->size); - fprintf(stderr, - "offset %p %d is of distance %d to next block; append? %d\n", - c->jumpOffsetHead, c->jumpOffsetHead->offset, v, append); - } - } - - if (append) { -#ifndef NDEBUG - int32_t v = (b->start + b->size) - - (c->jumpOffsetHead->offset + c->jumpOffsetHead->block->start); - - expect(c, bounded(2, 16, v)); -#endif // not NDEBUG - - appendJumpEvent(c, b, b->size, c->jumpOffsetHead, c->jumpOffsetTail); - - if (DebugJumps) { - for (JumpOffset* o = c->jumpOffsetHead; o; o = o->next) { - fprintf(stderr, - "include %p %d in jump event %p at offset %d in block %p\n", - o, o->offset, b->jumpEventTail, b->size, b); - } - } - - c->jumpOffsetHead = 0; - c->jumpOffsetTail = 0; - } - } -} - -inline unsigned -index(ArchitectureContext*, UnaryOperation operation, OperandType operand) -{ - return operation + (UnaryOperationCount * operand); -} - -inline unsigned -index(ArchitectureContext*, - BinaryOperation operation, - OperandType operand1, - OperandType operand2) -{ - return operation - + (BinaryOperationCount * operand1) - + (BinaryOperationCount * OperandTypeCount * operand2); -} - -bool -isBranch(TernaryOperation op) -{ - return op > FloatMin; -} - -bool -isFloatBranch(TernaryOperation op) -{ - return op > JumpIfNotEqual; -} - -inline unsigned -index(ArchitectureContext* c UNUSED, - TernaryOperation operation, - OperandType operand1) -{ - assert(c, not isBranch(operation)); - - return operation + (NonBranchTernaryOperationCount * operand1); -} - -unsigned -branchIndex(ArchitectureContext* c UNUSED, OperandType operand1, - OperandType operand2) -{ - return operand1 + (OperandTypeCount * operand2); -} - -// BEGIN OPERATION COMPILERS - -using namespace isa; - -inline void emit(Context* con, int code) { con->code.append4(targetV4(code)); } -inline int newTemp(Context* con) { return con->client->acquireTemporary(); } -inline void freeTemp(Context* con, int r) { con->client->releaseTemporary(r); } -inline int64_t getValue(Assembler::Constant* c) { return c->value->value(); } - -inline void -write4(uint8_t* dst, uint32_t v) -{ - memcpy(dst, &v, 4); -} - -void -andC(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst); - -void shiftLeftR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - if(size == 8) { - Assembler::Register Tmp(newTemp(con), newTemp(con)); Assembler::Register* tmp = &Tmp; - emit(con, subfic(tmp->high, a->low, 32)); - emit(con, slw(t->high, b->high, a->low)); - emit(con, srw(tmp->low, b->low, tmp->high)); - emit(con, or_(t->high, t->high, tmp->low)); - emit(con, addi(tmp->high, a->low, -32)); - emit(con, slw(tmp->low, b->low, tmp->high)); - emit(con, or_(t->high, t->high, tmp->low)); - emit(con, slw(t->low, b->low, a->low)); - freeTemp(con, tmp->high); freeTemp(con, tmp->low); - } else { - emit(con, slw(t->low, b->low, a->low)); - } -} - -void -moveRR(Context* c, unsigned srcSize, Assembler::Register* src, - unsigned dstSize, Assembler::Register* dst); - -void shiftLeftC(Context* con, unsigned size, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - int sh = getValue(a); - if (size == 8) { - sh &= 0x3F; - if (sh) { - if (sh < 32) { - emit(con, rlwinm(t->high,b->high,sh,0,31-sh)); - emit(con, rlwimi(t->high,b->low,sh,32-sh,31)); - emit(con, slwi(t->low, b->low, sh)); - } else { - emit(con, rlwinm(t->high,b->low,sh-32,0,63-sh)); - emit(con, li(t->low,0)); - } - } else { - moveRR(con, size, b, size, t); - } - } else { - emit(con, slwi(t->low, b->low, sh & 0x1F)); - } -} - -void shiftRightR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - if(size == 8) { - Assembler::Register Tmp(newTemp(con), newTemp(con)); Assembler::Register* tmp = &Tmp; - emit(con, subfic(tmp->high, a->low, 32)); - emit(con, srw(t->low, b->low, a->low)); - emit(con, slw(tmp->low, b->high, tmp->high)); - emit(con, or_(t->low, t->low, tmp->low)); - emit(con, addic(tmp->high, a->low, -32)); - emit(con, sraw(tmp->low, b->high, tmp->high)); - emit(con, ble(8)); - emit(con, ori(t->low, tmp->low, 0)); - emit(con, sraw(t->high, b->high, a->low)); - freeTemp(con, tmp->high); freeTemp(con, tmp->low); - } else { - emit(con, sraw(t->low, b->low, a->low)); - } -} - -void shiftRightC(Context* con, unsigned size, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - int sh = getValue(a); - if(size == 8) { - sh &= 0x3F; - if (sh) { - if (sh < 32) { - emit(con, rlwinm(t->low,b->low,32-sh,sh,31)); - emit(con, rlwimi(t->low,b->high,32-sh,0,sh-1)); - emit(con, srawi(t->high,b->high,sh)); - } else { - emit(con, srawi(t->high,b->high,31)); - emit(con, srawi(t->low,b->high,sh-32)); - } - } else { - moveRR(con, size, b, size, t); - } - } else { - emit(con, srawi(t->low, b->low, sh & 0x1F)); - } -} - -void unsignedShiftRightR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) -{ - emit(con, srw(t->low, b->low, a->low)); - if(size == 8) { - Assembler::Register Tmp(newTemp(con), newTemp(con)); Assembler::Register* tmp = &Tmp; - emit(con, subfic(tmp->high, a->low, 32)); - emit(con, slw(tmp->low, b->high, tmp->high)); - emit(con, or_(t->low, t->low, tmp->low)); - emit(con, addi(tmp->high, a->low, -32)); - emit(con, srw(tmp->low, b->high, tmp->high)); - emit(con, or_(t->low, t->low, tmp->low)); - emit(con, srw(t->high, b->high, a->low)); - freeTemp(con, tmp->high); freeTemp(con, tmp->low); - } -} - -void unsignedShiftRightC(Context* con, unsigned size, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) -{ - int sh = getValue(a); - if (size == 8) { - if (sh & 0x3F) { - if (sh == 32) { - Assembler::Register high(b->high); - moveRR(con, 4, &high, 4, t); - emit(con, li(t->high,0)); - } else if (sh < 32) { - emit(con, srwi(t->low, b->low, sh)); - emit(con, rlwimi(t->low,b->high,32-sh,0,sh-1)); - emit(con, rlwinm(t->high,b->high,32-sh,sh,31)); - } else { - emit(con, rlwinm(t->low,b->high,64-sh,sh-32,31)); - emit(con, li(t->high,0)); - } - } else { - moveRR(con, size, b, size, t); - } - } else { - if (sh & 0x1F) { - emit(con, srwi(t->low, b->low, sh & 0x1F)); - } else { - moveRR(con, size, b, size, t); - } - } -} - -void -updateImmediate(System* s, void* dst, int32_t src, unsigned size, bool address) -{ - switch (size) { - case 4: { - int32_t* p = static_cast(dst); - int r = (targetV4(p[1]) >> 21) & 31; - - if (address) { - p[0] = targetV4(lis(r, ha16(src))); - p[1] |= targetV4(src & 0xFFFF); - } else { - p[0] = targetV4(lis(r, src >> 16)); - p[1] = targetV4(ori(r, r, src)); - } - } break; - - default: abort(s); - } -} - -class ImmediateListener: public Promise::Listener { - public: - ImmediateListener(System* s, void* dst, unsigned size, unsigned offset, - bool address): - s(s), dst(dst), size(size), offset(offset), address(address) - { } - - virtual bool resolve(int64_t value, void** location) { - updateImmediate(s, dst, value, size, address); - if (location) *location = static_cast(dst) + offset; - return false; - } - - System* s; - void* dst; - unsigned size; - unsigned offset; - bool address; -}; - -class ImmediateTask: public Task { - public: - ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, - unsigned promiseOffset, bool address): - Task(next), - promise(promise), - offset(offset), - size(size), - promiseOffset(promiseOffset), - address(address) - { } - - virtual void run(Context* c) { - if (promise->resolved()) { - updateImmediate - (c->s, c->result + offset->value(), promise->value(), size, address); - } else { - new (promise->listen(sizeof(ImmediateListener))) ImmediateListener - (c->s, c->result + offset->value(), size, promiseOffset, address); - } - } - - Promise* promise; - Promise* offset; - unsigned size; - unsigned promiseOffset; - bool address; -}; - -void -appendImmediateTask(Context* c, Promise* promise, Promise* offset, - unsigned size, unsigned promiseOffset, bool address) -{ - c->tasks = new(c->zone) ImmediateTask(c->tasks, promise, offset, size, promiseOffset, address); -} - -class ConstantPoolEntry: public Promise { - public: - ConstantPoolEntry(Context* c, Promise* constant): - c(c), constant(constant), next(c->constantPool), address(0) - { - c->constantPool = this; - ++ c->constantPoolCount; - } - - virtual int64_t value() { - assert(c, resolved()); - - return reinterpret_cast(address); - } - - virtual bool resolved() { - return address != 0; - } - - Context* c; - Promise* constant; - ConstantPoolEntry* next; - void* address; -}; - -ConstantPoolEntry* -appendConstantPoolEntry(Context* c, Promise* constant) -{ - return new (c->zone) ConstantPoolEntry(c, constant); -} - -void -jumpR(Context* c, unsigned size UNUSED, Assembler::Register* target) -{ - assert(c, size == TargetBytesPerWord); - - emit(c, mtctr(target->low)); - emit(c, bctr()); -} - -void -swapRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == TargetBytesPerWord); - assert(c, bSize == TargetBytesPerWord); - - Assembler::Register tmp(c->client->acquireTemporary()); - moveRR(c, aSize, a, bSize, &tmp); - moveRR(c, bSize, b, aSize, a); - moveRR(c, bSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); -} - -void -moveRR(Context* c, unsigned srcSize, Assembler::Register* src, - unsigned dstSize, Assembler::Register* dst) -{ - switch (srcSize) { - case 1: - emit(c, extsb(dst->low, src->low)); - break; - - case 2: - emit(c, extsh(dst->low, src->low)); - break; - - case 4: - case 8: - if (srcSize == 4 and dstSize == 8) { - moveRR(c, 4, src, 4, dst); - emit(c, srawi(dst->high, src->low, 31)); - } else if (srcSize == 8 and dstSize == 8) { - Assembler::Register srcHigh(src->high); - Assembler::Register dstHigh(dst->high); - - if (src->high == dst->low) { - if (src->low == dst->high) { - swapRR(c, 4, src, 4, dst); - } else { - moveRR(c, 4, &srcHigh, 4, &dstHigh); - moveRR(c, 4, src, 4, dst); - } - } else { - moveRR(c, 4, src, 4, dst); - moveRR(c, 4, &srcHigh, 4, &dstHigh); - } - } else if (src->low != dst->low) { - emit(c, mr(dst->low, src->low)); - } - break; - - default: abort(c); - } -} - -void -moveZRR(Context* c, unsigned srcSize, Assembler::Register* src, - unsigned, Assembler::Register* dst) -{ - switch (srcSize) { - case 2: - emit(c, andi(dst->low, src->low, 0xFFFF)); - break; - - default: abort(c); - } -} - -void -moveCR2(Context* c, unsigned, Assembler::Constant* src, - unsigned dstSize, Assembler::Register* dst, unsigned promiseOffset) -{ - if (dstSize <= 4) { - if (src->value->resolved()) { - int32_t v = src->value->value(); - if (isInt16(v)) { - emit(c, li(dst->low, v)); - } else { - emit(c, lis(dst->low, v >> 16)); - emit(c, ori(dst->low, dst->low, v)); - } - } else { - appendImmediateTask - (c, src->value, offset(c), TargetBytesPerWord, promiseOffset, false); - emit(c, lis(dst->low, 0)); - emit(c, ori(dst->low, dst->low, 0)); - } - } else { - abort(c); // todo - } -} - -void -moveCR(Context* c, unsigned srcSize, Assembler::Constant* src, - unsigned dstSize, Assembler::Register* dst) -{ - moveCR2(c, srcSize, src, dstSize, dst, 0); -} - -void addR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if(size == 8) { - emit(con, addc(t->low, a->low, b->low)); - emit(con, adde(t->high, a->high, b->high)); - } else { - emit(con, add(t->low, a->low, b->low)); - } -} - -void addC(Context* con, unsigned size, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) { - assert(con, size == TargetBytesPerWord); - - int32_t i = getValue(a); - if(i) { - emit(con, addi(t->low, b->low, lo16(i))); - if(not isInt16(i)) - emit(con, addis(t->low, t->low, hi16(i) + carry16(i))); - } else { - moveRR(con, size, b, size, t); - } -} - -void subR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if(size == 8) { - emit(con, subfc(t->low, a->low, b->low)); - emit(con, subfe(t->high, a->high, b->high)); - } else { - emit(con, subf(t->low, a->low, b->low)); - } -} - -void subC(Context* c, unsigned size, Assembler::Constant* a, Assembler::Register* b, Assembler::Register* t) { - assert(c, size == TargetBytesPerWord); - - ResolvedPromise promise(- a->value->value()); - Assembler::Constant constant(&promise); - addC(c, size, &constant, b, t); -} - -void multiplyR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - if(size == 8) { - bool useTemporaries = b->low == t->low; - int tmpLow; - int tmpHigh; - if (useTemporaries) { - tmpLow = con->client->acquireTemporary(); - tmpHigh = con->client->acquireTemporary(); - } else { - tmpLow = t->low; - tmpHigh = t->high; - } - - emit(con, mullw(tmpHigh, a->high, b->low)); - emit(con, mullw(tmpLow, a->low, b->high)); - emit(con, add(t->high, tmpHigh, tmpLow)); - emit(con, mulhwu(tmpLow, a->low, b->low)); - emit(con, add(t->high, t->high, tmpLow)); - emit(con, mullw(t->low, a->low, b->low)); - - if (useTemporaries) { - con->client->releaseTemporary(tmpLow); - con->client->releaseTemporary(tmpHigh); - } - } else { - emit(con, mullw(t->low, a->low, b->low)); - } -} - -void divideR(Context* con, unsigned size UNUSED, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - assert(con, size == 4); - emit(con, divw(t->low, b->low, a->low)); -} - -void remainderR(Context* con, unsigned size, Assembler::Register* a, Assembler::Register* b, Assembler::Register* t) { - bool useTemporary = b->low == t->low; - Assembler::Register tmp(t->low); - if (useTemporary) { - tmp.low = con->client->acquireTemporary(); - } - - divideR(con, size, a, b, &tmp); - multiplyR(con, size, a, &tmp, &tmp); - subR(con, size, &tmp, b, t); - - if (useTemporary) { - con->client->releaseTemporary(tmp.low); - } -} - -int -normalize(Context* c, int offset, int index, unsigned scale, - bool* preserveIndex, bool* release) -{ - if (offset != 0 or scale != 1) { - Assembler::Register normalizedIndex - (*preserveIndex ? c->client->acquireTemporary() : index); - - if (*preserveIndex) { - *release = true; - *preserveIndex = false; - } else { - *release = false; - } - - int scaled; - - if (scale != 1) { - Assembler::Register unscaledIndex(index); - - ResolvedPromise scalePromise(log(scale)); - Assembler::Constant scaleConstant(&scalePromise); - - shiftLeftC(c, TargetBytesPerWord, &scaleConstant, - &unscaledIndex, &normalizedIndex); - - scaled = normalizedIndex.low; - } else { - scaled = index; - } - - if (offset != 0) { - Assembler::Register untranslatedIndex(scaled); - - ResolvedPromise offsetPromise(offset); - Assembler::Constant offsetConstant(&offsetPromise); - - addC(c, TargetBytesPerWord, &offsetConstant, - &untranslatedIndex, &normalizedIndex); - } - - return normalizedIndex.low; - } else { - *release = false; - return index; - } -} - -void -store(Context* c, unsigned size, Assembler::Register* src, - int base, int offset, int index, unsigned scale, bool preserveIndex) -{ - if (index != NoRegister) { - bool release; - int normalized = normalize - (c, offset, index, scale, &preserveIndex, &release); - - switch (size) { - case 1: - emit(c, stbx(src->low, base, normalized)); - break; - - case 2: - emit(c, sthx(src->low, base, normalized)); - break; - - case 4: - emit(c, stwx(src->low, base, normalized)); - break; - - case 8: { - Assembler::Register srcHigh(src->high); - store(c, 4, &srcHigh, base, 0, normalized, 1, preserveIndex); - store(c, 4, src, base, 4, normalized, 1, preserveIndex); - } break; - - default: abort(c); - } - - if (release) c->client->releaseTemporary(normalized); - } else { - switch (size) { - case 1: - emit(c, stb(src->low, base, offset)); - break; - - case 2: - emit(c, sth(src->low, base, offset)); - break; - - case 4: - emit(c, stw(src->low, base, offset)); - break; - - case 8: { - Assembler::Register srcHigh(src->high); - store(c, 4, &srcHigh, base, offset, NoRegister, 1, false); - store(c, 4, src, base, offset + 4, NoRegister, 1, false); - } break; - - default: abort(c); - } - } -} - -void -moveRM(Context* c, unsigned srcSize, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Memory* dst) -{ - assert(c, srcSize == dstSize); - - store(c, srcSize, src, dst->base, dst->offset, dst->index, dst->scale, true); -} - -void -moveAndUpdateRM(Context* c, unsigned srcSize UNUSED, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Memory* dst) -{ - assert(c, srcSize == TargetBytesPerWord); - assert(c, dstSize == TargetBytesPerWord); - - if (dst->index == NoRegister) { - emit(c, stwu(src->low, dst->base, dst->offset)); - } else { - assert(c, dst->offset == 0); - assert(c, dst->scale == 1); - - emit(c, stwux(src->low, dst->base, dst->index)); - } -} - -void -load(Context* c, unsigned srcSize, int base, int offset, int index, - unsigned scale, unsigned dstSize, Assembler::Register* dst, - bool preserveIndex, bool signExtend) -{ - if (index != NoRegister) { - bool release; - int normalized = normalize - (c, offset, index, scale, &preserveIndex, &release); - - switch (srcSize) { - case 1: - emit(c, lbzx(dst->low, base, normalized)); - if (signExtend) { - emit(c, extsb(dst->low, dst->low)); - } - break; - - case 2: - if (signExtend) { - emit(c, lhax(dst->low, base, normalized)); - } else { - emit(c, lhzx(dst->low, base, normalized)); - } - break; - - case 4: - case 8: { - if (srcSize == 4 and dstSize == 8) { - load(c, 4, base, 0, normalized, 1, 4, dst, preserveIndex, false); - moveRR(c, 4, dst, 8, dst); - } else if (srcSize == 8 and dstSize == 8) { - Assembler::Register dstHigh(dst->high); - load(c, 4, base, 0, normalized, 1, 4, &dstHigh, preserveIndex, false); - load(c, 4, base, 4, normalized, 1, 4, dst, preserveIndex, false); - } else { - emit(c, lwzx(dst->low, base, normalized)); - } - } break; - - default: abort(c); - } - - if (release) c->client->releaseTemporary(normalized); - } else { - switch (srcSize) { - case 1: - emit(c, lbz(dst->low, base, offset)); - if (signExtend) { - emit(c, extsb(dst->low, dst->low)); - } - break; - - case 2: - if (signExtend) { - emit(c, lha(dst->low, base, offset)); - } else { - emit(c, lha(dst->low, base, offset)); - } - break; - - case 4: - emit(c, lwz(dst->low, base, offset)); - break; - - case 8: { - if (dstSize == 8) { - Assembler::Register dstHigh(dst->high); - load(c, 4, base, offset, NoRegister, 1, 4, &dstHigh, false, false); - load(c, 4, base, offset + 4, NoRegister, 1, 4, dst, false, false); - } else { - emit(c, lwzx(dst->low, base, offset)); - } - } break; - - default: abort(c); - } - } -} - -void -moveMR(Context* c, unsigned srcSize, Assembler::Memory* src, - unsigned dstSize, Assembler::Register* dst) -{ - load(c, srcSize, src->base, src->offset, src->index, src->scale, - dstSize, dst, true, true); -} - -void -moveZMR(Context* c, unsigned srcSize, Assembler::Memory* src, - unsigned dstSize, Assembler::Register* dst) -{ - load(c, srcSize, src->base, src->offset, src->index, src->scale, - dstSize, dst, true, false); -} - -void -andR(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - andR(c, 4, a, b, dst); - andR(c, 4, &ah, &bh, &dh); - } else { - emit(c, and_(dst->low, a->low, b->low)); - } -} - -void -andC(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - int64_t v = a->value->value(); - - if (size == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - andC(c, 4, &al, b, dst); - andC(c, 4, &ah, &bh, &dh); - } else { - // bitmasks of the form regex 0*1*0* can be handled in a single - // rlwinm instruction, hence the following: - - uint32_t v32 = static_cast(v); - unsigned state = 0; - unsigned start = 0; - unsigned end = 31; - for (unsigned i = 0; i < 32; ++i) { - unsigned bit = (v32 >> i) & 1; - switch (state) { - case 0: - if (bit) { - start = i; - state = 1; - } - break; - - case 1: - if (bit == 0) { - end = i - 1; - state = 2; - } - break; - - case 2: - if (bit) { - // not in 0*1*0* form. We can only use andi(s) if either - // the topmost or bottommost 16 bits are zero. - - if ((v32 >> 16) == 0) { - emit(c, andi(dst->low, b->low, v32)); - } else if ((v32 & 0xFFFF) == 0) { - emit(c, andis(dst->low, b->low, v32 >> 16)); - } else { - bool useTemporary = b->low == dst->low; - Assembler::Register tmp(dst->low); - if (useTemporary) { - tmp.low = c->client->acquireTemporary(); - } - - moveCR(c, 4, a, 4, &tmp); - andR(c, 4, b, &tmp, dst); - - if (useTemporary) { - c->client->releaseTemporary(tmp.low); - } - } - return; - } - break; - } - } - - if (state) { - if (start != 0 or end != 31) { - emit(c, rlwinm(dst->low, b->low, 0, 31 - end, 31 - start)); - } else { - moveRR(c, 4, b, 4, dst); - } - } else { - emit(c, li(dst->low, 0)); - } - } -} - -void -orR(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - orR(c, 4, a, b, dst); - orR(c, 4, &ah, &bh, &dh); - } else { - emit(c, or_(dst->low, a->low, b->low)); - } -} - -void -orC(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - int64_t v = a->value->value(); - - if (size == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - orC(c, 4, &al, b, dst); - orC(c, 4, &ah, &bh, &dh); - } else { - emit(c, ori(b->low, dst->low, v)); - if (v >> 16) { - emit(c, oris(dst->low, dst->low, v >> 16)); - } - } -} - -void -xorR(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b, Assembler::Register* dst) -{ - if (size == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - xorR(c, 4, a, b, dst); - xorR(c, 4, &ah, &bh, &dh); - } else { - emit(c, xor_(dst->low, a->low, b->low)); - } -} - -void -xorC(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b, Assembler::Register* dst) -{ - uint64_t v = a->value->value(); - - if (size == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - Assembler::Register dh(dst->high); - - xorC(c, 4, &al, b, dst); - xorC(c, 4, &ah, &bh, &dh); - } else { - if (v >> 16) { - emit(c, xoris(b->low, dst->low, v >> 16)); - emit(c, xori(dst->low, dst->low, v)); - } else { - emit(c, xori(b->low, dst->low, v)); - } - } -} - -void -moveAR2(Context* c, unsigned srcSize UNUSED, Assembler::Address* src, - unsigned dstSize, Assembler::Register* dst, unsigned promiseOffset) -{ - assert(c, srcSize == 4 and dstSize == 4); - - Assembler::Memory memory(dst->low, 0, -1, 0); - - appendImmediateTask - (c, src->address, offset(c), TargetBytesPerWord, promiseOffset, true); - - emit(c, lis(dst->low, 0)); - moveMR(c, dstSize, &memory, dstSize, dst); -} - -void -moveAR(Context* c, unsigned srcSize, Assembler::Address* src, - unsigned dstSize, Assembler::Register* dst) -{ - moveAR2(c, srcSize, src, dstSize, dst, 0); -} - -void -compareRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == 4 and bSize == 4); - - emit(c, cmpw(b->low, a->low)); -} - -void -compareCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == 4 and bSize == 4); - - if (a->value->resolved() and isInt16(a->value->value())) { - emit(c, cmpwi(b->low, a->value->value())); - } else { - Assembler::Register tmp(c->client->acquireTemporary()); - moveCR(c, aSize, a, bSize, &tmp); - compareRR(c, bSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } -} - -void -compareCM(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Memory* b) -{ - assert(c, aSize == 4 and bSize == 4); - - Assembler::Register tmp(c->client->acquireTemporary()); - moveMR(c, bSize, b, bSize, &tmp); - compareCR(c, aSize, a, bSize, &tmp); - c->client->releaseTemporary(tmp.low); -} - -void -compareRM(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Memory* b) -{ - assert(c, aSize == 4 and bSize == 4); - - Assembler::Register tmp(c->client->acquireTemporary()); - moveMR(c, bSize, b, bSize, &tmp); - compareRR(c, aSize, a, bSize, &tmp); - c->client->releaseTemporary(tmp.low); -} - -void -compareUnsignedRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == 4 and bSize == 4); - - emit(c, cmplw(b->low, a->low)); -} - -void -compareUnsignedCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == 4 and bSize == 4); - - if (a->value->resolved() and (a->value->value() >> 16) == 0) { - emit(c, cmplwi(b->low, a->value->value())); - } else { - Assembler::Register tmp(c->client->acquireTemporary()); - moveCR(c, aSize, a, bSize, &tmp); - compareUnsignedRR(c, bSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } -} - -int32_t -branch(Context* c, TernaryOperation op) -{ - switch (op) { - case JumpIfEqual: - return beq(0); - - case JumpIfNotEqual: - return bne(0); - - case JumpIfLess: - return blt(0); - - case JumpIfGreater: - return bgt(0); - - case JumpIfLessOrEqual: - return ble(0); - - case JumpIfGreaterOrEqual: - return bge(0); - - default: - abort(c); - } -} - -void -conditional(Context* c, int32_t branch, Assembler::Constant* target) -{ - appendOffsetTask(c, target->value, offset(c), true); - emit(c, branch); -} - -void -branch(Context* c, TernaryOperation op, Assembler::Constant* target) -{ - conditional(c, branch(c, op), target); -} - -void -branchLong(Context* c, TernaryOperation op, Assembler::Operand* al, - Assembler::Operand* ah, Assembler::Operand* bl, - Assembler::Operand* bh, Assembler::Constant* target, - BinaryOperationType compareSigned, - BinaryOperationType compareUnsigned) -{ - compareSigned(c, 4, ah, 4, bh); - - unsigned next = 0; - - switch (op) { - case JumpIfEqual: - next = c->code.length(); - emit(c, bne(0)); - - compareSigned(c, 4, al, 4, bl); - conditional(c, beq(0), target); - break; - - case JumpIfNotEqual: - conditional(c, bne(0), target); - - compareSigned(c, 4, al, 4, bl); - conditional(c, bne(0), target); - break; - - case JumpIfLess: - conditional(c, blt(0), target); - - next = c->code.length(); - emit(c, bgt(0)); - - compareUnsigned(c, 4, al, 4, bl); - conditional(c, blt(0), target); - break; - - case JumpIfGreater: - conditional(c, bgt(0), target); - - next = c->code.length(); - emit(c, blt(0)); - - compareUnsigned(c, 4, al, 4, bl); - conditional(c, bgt(0), target); - break; - - case JumpIfLessOrEqual: - conditional(c, blt(0), target); - - next = c->code.length(); - emit(c, bgt(0)); - - compareUnsigned(c, 4, al, 4, bl); - conditional(c, ble(0), target); - break; - - case JumpIfGreaterOrEqual: - conditional(c, bgt(0), target); - - next = c->code.length(); - emit(c, blt(0)); - - compareUnsigned(c, 4, al, 4, bl); - conditional(c, bge(0), target); - break; - - default: - abort(c); - } - - if (next) { - updateOffset - (c->s, c->code.data + next, true, reinterpret_cast - (c->code.data + c->code.length()), 0); - } -} - -void -branchRR(Context* c, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Register* b, - Assembler::Constant* target) -{ - if (size > TargetBytesPerWord) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR), - CAST2(compareUnsignedRR)); - } else { - compareRR(c, size, a, size, b); - branch(c, op, target); - } -} - -void -branchCR(Context* c, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Register* b, - Assembler::Constant* target) -{ - if (size > TargetBytesPerWord) { - int64_t v = a->value->value(); - - ResolvedPromise low(v & ~static_cast(0)); - Assembler::Constant al(&low); - - ResolvedPromise high((v >> 32) & ~static_cast(0)); - Assembler::Constant ah(&high); - - Assembler::Register bh(b->high); - - branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR), - CAST2(compareUnsignedCR)); - } else { - compareCR(c, size, a, size, b); - branch(c, op, target); - } -} - -void -branchRM(Context* c, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(c, size <= TargetBytesPerWord); - - compareRM(c, size, a, size, b); - branch(c, op, target); -} - -void -branchCM(Context* c, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(c, size <= TargetBytesPerWord); - - compareCM(c, size, a, size, b); - branch(c, op, target); -} - -ShiftMaskPromise* -shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) -{ - return new (c->zone) ShiftMaskPromise(base, shift, mask); -} - -void -moveCM(Context* c, unsigned srcSize, Assembler::Constant* src, - unsigned dstSize, Assembler::Memory* dst) -{ - switch (dstSize) { - case 8: { - Assembler::Constant srcHigh - (shiftMaskPromise(c, src->value, 32, 0xFFFFFFFF)); - Assembler::Constant srcLow - (shiftMaskPromise(c, src->value, 0, 0xFFFFFFFF)); - - Assembler::Memory dstLow - (dst->base, dst->offset + 4, dst->index, dst->scale); - - moveCM(c, 4, &srcLow, 4, &dstLow); - moveCM(c, 4, &srcHigh, 4, dst); - } break; - - default: - Assembler::Register tmp(c->client->acquireTemporary()); - moveCR(c, srcSize, src, dstSize, &tmp); - moveRM(c, dstSize, &tmp, dstSize, dst); - c->client->releaseTemporary(tmp.low); - } -} - -void -negateRR(Context* c, unsigned srcSize, Assembler::Register* src, - unsigned dstSize UNUSED, Assembler::Register* dst) -{ - assert(c, srcSize == dstSize); - - if (srcSize == 8) { - Assembler::Register dstHigh(dst->high); - - emit(c, subfic(dst->low, src->low, 0)); - emit(c, subfze(dst->high, src->high)); - } else { - emit(c, neg(dst->low, src->low)); - } -} - -void -callR(Context* c, unsigned size UNUSED, Assembler::Register* target) -{ - assert(c, size == TargetBytesPerWord); - - emit(c, mtctr(target->low)); - emit(c, bctrl()); -} - -void -callC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - appendOffsetTask(c, target->value, offset(c), false); - emit(c, bl(0)); -} - -void -longCallC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - Assembler::Register tmp(0); - moveCR2(c, TargetBytesPerWord, target, TargetBytesPerWord, &tmp, 12); - callR(c, TargetBytesPerWord, &tmp); -} - -void -alignedLongCallC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - Assembler::Register tmp(c->client->acquireTemporary()); - Assembler::Address address(appendConstantPoolEntry(c, target->value)); - moveAR2(c, TargetBytesPerWord, &address, TargetBytesPerWord, &tmp, 12); - callR(c, TargetBytesPerWord, &tmp); - c->client->releaseTemporary(tmp.low); -} - -void -longJumpC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - Assembler::Register tmp(0); - moveCR2(c, TargetBytesPerWord, target, TargetBytesPerWord, &tmp, 12); - jumpR(c, TargetBytesPerWord, &tmp); -} - -void -alignedLongJumpC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - Assembler::Register tmp(c->client->acquireTemporary()); - Assembler::Address address(appendConstantPoolEntry(c, target->value)); - moveAR2(c, TargetBytesPerWord, &address, TargetBytesPerWord, &tmp, 12); - jumpR(c, TargetBytesPerWord, &tmp); - c->client->releaseTemporary(tmp.low); -} - -void -jumpC(Context* c, unsigned size UNUSED, Assembler::Constant* target) -{ - assert(c, size == TargetBytesPerWord); - - appendOffsetTask(c, target->value, offset(c), false); - emit(c, b(0)); -} - -void -return_(Context* c) -{ - emit(c, blr()); -} - -void -trap(Context* c) -{ - emit(c, trap()); -} - -void -memoryBarrier(Context* c) -{ - emit(c, sync(0)); -} - -// END OPERATION COMPILERS - -unsigned -argumentFootprint(unsigned footprint) -{ - return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); -} - -void -nextFrame(ArchitectureContext* c UNUSED, int32_t* start, unsigned size, - unsigned footprint, void* link, bool, - unsigned targetParameterFootprint, void** ip, void** stack) -{ - assert(c, *ip >= start); - assert(c, *ip <= start + (size / BytesPerWord)); - - int32_t* instruction = static_cast(*ip); - - if ((*start >> 26) == 32) { - // skip stack overflow check - start += 3; - } - - if (instruction <= start + 2 - or *instruction == lwz(0, 1, 8) - or *instruction == mtlr(0) - or *instruction == blr()) - { - *ip = link; - return; - } - - unsigned offset = footprint; - - if (TailCalls) { - if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { - offset += argumentFootprint(targetParameterFootprint) - - StackAlignmentInWords; - } - - // check for post-non-tail-call stack adjustment of the form "lwzx - // r0,0(r1); stwu r0,offset(r1)": - if (instruction < start + (size / BytesPerWord) - 1 - and (static_cast(instruction[1]) >> 16) == 0x9401) - { - offset += static_cast(instruction[1]) / BytesPerWord; - } else if ((static_cast(*instruction) >> 16) == 0x9401) { - offset += static_cast(*instruction) / BytesPerWord; - } - - // todo: check for and handle tail calls - } - - *ip = static_cast(*stack)[offset + ReturnAddressOffset]; - *stack = static_cast(*stack) + offset; -} - -void -populateTables(ArchitectureContext* c) -{ - const OperandType C = ConstantOperand; - const OperandType A = AddressOperand; - const OperandType R = RegisterOperand; - const OperandType M = MemoryOperand; - - OperationType* zo = c->operations; - UnaryOperationType* uo = c->unaryOperations; - BinaryOperationType* bo = c->binaryOperations; - TernaryOperationType* to = c->ternaryOperations; - BranchOperationType* bro = c->branchOperations; - - zo[Return] = return_; - zo[LoadBarrier] = memoryBarrier; - zo[StoreStoreBarrier] = memoryBarrier; - zo[StoreLoadBarrier] = memoryBarrier; - zo[Trap] = trap; - - uo[index(c, LongCall, C)] = CAST1(longCallC); - - uo[index(c, AlignedLongCall, C)] = CAST1(alignedLongCallC); - - uo[index(c, LongJump, C)] = CAST1(longJumpC); - - uo[index(c, AlignedLongJump, C)] = CAST1(alignedLongJumpC); - - uo[index(c, Jump, R)] = CAST1(jumpR); - uo[index(c, Jump, C)] = CAST1(jumpC); - - uo[index(c, AlignedJump, R)] = CAST1(jumpR); - uo[index(c, AlignedJump, C)] = CAST1(jumpC); - - uo[index(c, Call, C)] = CAST1(callC); - uo[index(c, Call, R)] = CAST1(callR); - - uo[index(c, AlignedCall, C)] = CAST1(callC); - uo[index(c, AlignedCall, R)] = CAST1(callR); - - bo[index(c, Move, R, R)] = CAST2(moveRR); - bo[index(c, Move, C, R)] = CAST2(moveCR); - bo[index(c, Move, C, M)] = CAST2(moveCM); - bo[index(c, Move, M, R)] = CAST2(moveMR); - bo[index(c, Move, R, M)] = CAST2(moveRM); - bo[index(c, Move, A, R)] = CAST2(moveAR); - - bo[index(c, MoveZ, R, R)] = CAST2(moveZRR); - bo[index(c, MoveZ, M, R)] = CAST2(moveZMR); - bo[index(c, MoveZ, C, R)] = CAST2(moveCR); - - bo[index(c, Negate, R, R)] = CAST2(negateRR); - - to[index(c, Add, R)] = CAST3(addR); - to[index(c, Add, C)] = CAST3(addC); - - to[index(c, Subtract, R)] = CAST3(subR); - to[index(c, Subtract, C)] = CAST3(subC); - - to[index(c, Multiply, R)] = CAST3(multiplyR); - - to[index(c, Divide, R)] = CAST3(divideR); - - to[index(c, Remainder, R)] = CAST3(remainderR); - - to[index(c, ShiftLeft, R)] = CAST3(shiftLeftR); - to[index(c, ShiftLeft, C)] = CAST3(shiftLeftC); - - to[index(c, ShiftRight, R)] = CAST3(shiftRightR); - to[index(c, ShiftRight, C)] = CAST3(shiftRightC); - - to[index(c, UnsignedShiftRight, R)] = CAST3(unsignedShiftRightR); - to[index(c, UnsignedShiftRight, C)] = CAST3(unsignedShiftRightC); - - to[index(c, And, C)] = CAST3(andC); - to[index(c, And, R)] = CAST3(andR); - - to[index(c, Or, C)] = CAST3(orC); - to[index(c, Or, R)] = CAST3(orR); - - to[index(c, Xor, C)] = CAST3(xorC); - to[index(c, Xor, R)] = CAST3(xorR); - - bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR); - bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR); - bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM); - bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM); -} - -class MyArchitecture: public Assembler::Architecture { - public: - MyArchitecture(System* system): c(system), referenceCount(0) { - populateTables(&c); - } - - virtual unsigned floatRegisterSize() { - return 0; - } - - virtual uint32_t generalRegisterMask() { - return 0xFFFFFFFF; - } - - virtual uint32_t floatRegisterMask() { - return 0; - } - - virtual int scratch() { - return 31; - } - - virtual int stack() { - return StackRegister; - } - - virtual int thread() { - return ThreadRegister; - } - - virtual int returnLow() { - return 4; - } - - virtual int returnHigh() { - return (TargetBytesPerWord == 4 ? 3 : NoRegister); - } - - virtual int virtualCallTarget() { - return 4; - } - - virtual int virtualCallIndex() { - return 3; - } - - virtual bool bigEndian() { - return true; - } - - virtual uintptr_t maximumImmediateJump() { - return 0x1FFFFFF; - } - - virtual bool reserved(int register_) { - switch (register_) { - case 0: // r0 has special meaning in addi and other instructions - case StackRegister: - case ThreadRegister: -#ifndef __APPLE__ - // r2 is reserved for system uses on SYSV - case 2: -#endif - return true; - - default: - return false; - } - } - - virtual unsigned frameFootprint(unsigned footprint) { - return max(footprint, StackAlignmentInWords); - } - - virtual unsigned argumentFootprint(unsigned footprint) { - return ::argumentFootprint(footprint); - } - - virtual bool argumentAlignment() { - return AlignArguments; - } - - virtual bool argumentRegisterAlignment() { - return true; - } - - virtual unsigned argumentRegisterCount() { - return 8; - } - - virtual int argumentRegister(unsigned index) { - assert(&c, index < argumentRegisterCount()); - - return index + 3; - } - - virtual bool hasLinkRegister() { - return true; - } - - 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, - void* returnAddress, - void* newTarget) - { - switch (op) { - case Call: - case Jump: - case AlignedCall: - case AlignedJump: { - updateOffset(c.s, static_cast(returnAddress) - 4, false, - reinterpret_cast(newTarget), 0); - } break; - - case LongCall: - case LongJump: { - updateImmediate - (c.s, static_cast(returnAddress) - 12, - reinterpret_cast(newTarget), TargetBytesPerWord, false); - } break; - - case AlignedLongCall: - case AlignedLongJump: { - uint32_t* p = static_cast(returnAddress) - 4; - *reinterpret_cast(unha16(p[0] & 0xFFFF, p[1] & 0xFFFF)) - = newTarget; - } break; - - default: abort(&c); - } - } - - virtual unsigned constantCallSize() { - return 4; - } - - virtual void setConstant(void* dst, uint64_t constant) { - updateImmediate(c.s, dst, constant, TargetBytesPerWord, false); - } - - virtual unsigned alignFrameSize(unsigned sizeInWords) { - const unsigned alignment = StackAlignmentInWords; - return (ceiling(sizeInWords + FrameFooterSize, alignment) * alignment); - } - - virtual void nextFrame(void* start, unsigned size, unsigned footprint, - void* link, bool mostRecent, - unsigned targetParameterFootprint, void** ip, - void** stack) - { - ::nextFrame(&c, static_cast(start), size, footprint, link, - mostRecent, targetParameterFootprint, ip, stack); - } - - virtual void* frameIp(void* stack) { - return stack ? static_cast(stack)[ReturnAddressOffset] : 0; - } - - virtual unsigned frameHeaderSize() { - return 0; - } - - virtual unsigned frameReturnAddressSize() { - return 0; - } - - virtual unsigned frameFooterSize() { - return FrameFooterSize; - } - - virtual int returnAddressOffset() { - return ReturnAddressOffset; - } - - virtual int framePointerOffset() { - return 0; - } - - virtual BinaryOperation hasBinaryIntrinsic(Thread*, object) { - return NoBinaryOperation; - } - - virtual TernaryOperation hasTernaryIntrinsic(Thread*, object) { - return NoTernaryOperation; - } - - virtual bool alwaysCondensed(BinaryOperation) { - return false; - } - - virtual bool alwaysCondensed(TernaryOperation) { - return false; - } - - virtual void plan - (UnaryOperation, - unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, - bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *aRegisterMask = ~static_cast(0); - *thunk = false; - } - - virtual void planSource - (BinaryOperation op, - unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned, bool* thunk) - { - *aTypeMask = ~0; - *aRegisterMask = ~static_cast(0); - - *thunk = false; - - switch (op) { - case Negate: - *aTypeMask = (1 << RegisterOperand); - break; - - case Absolute: - case FloatAbsolute: - case FloatSquareRoot: - case FloatNegate: - case Float2Float: - case Float2Int: - case Int2Float: - *thunk = true; - break; - - default: - break; - } - } - - virtual void planDestination - (BinaryOperation op, - unsigned, uint8_t, uint64_t, - unsigned, uint8_t* bTypeMask, uint64_t* bRegisterMask) - { - *bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *bRegisterMask = ~static_cast(0); - - switch (op) { - case Negate: - *bTypeMask = (1 << RegisterOperand); - break; - - default: - break; - } - } - - virtual void planMove - (unsigned, uint8_t* srcTypeMask, uint64_t* srcRegisterMask, - uint8_t* tmpTypeMask, uint64_t* tmpRegisterMask, - uint8_t dstTypeMask, uint64_t) - { - *srcTypeMask = ~0; - *srcRegisterMask = ~static_cast(0); - - *tmpTypeMask = 0; - *tmpRegisterMask = 0; - - if (dstTypeMask & (1 << MemoryOperand)) { - // can't move directly from memory or constant to memory - *srcTypeMask = 1 << RegisterOperand; - *tmpTypeMask = 1 << RegisterOperand; - *tmpRegisterMask = ~static_cast(0); - } - } - - virtual void planSource - (TernaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned, uint8_t* bTypeMask, uint64_t* bRegisterMask, - unsigned, bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *aRegisterMask = ~static_cast(0); - - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = ~static_cast(0); - - *thunk = false; - - switch (op) { - case Add: - case Subtract: - if (aSize == 8) { - *aTypeMask = *bTypeMask = (1 << RegisterOperand); - } - break; - - case Multiply: - *aTypeMask = *bTypeMask = (1 << RegisterOperand); - break; - - case Divide: - case Remainder: - // todo: we shouldn't need to defer to thunks for integers which - // are smaller than or equal to tne native word size, but - // PowerPC doesn't generate traps for divide by zero, so we'd - // need to do the checks ourselves. Using an inline check - // should be faster than calling an out-of-line thunk, but the - // thunk is easier, so they's what we do for now. - if (true) {//if (TargetBytesPerWord == 4 and aSize == 8) { - *thunk = true; - } else { - *aTypeMask = (1 << RegisterOperand); - } - break; - - case FloatAdd: - case FloatSubtract: - case FloatMultiply: - case FloatDivide: - case FloatRemainder: - case JumpIfFloatEqual: - case JumpIfFloatNotEqual: - case JumpIfFloatLess: - case JumpIfFloatGreater: - case JumpIfFloatLessOrEqual: - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatLessOrUnordered: - case JumpIfFloatGreaterOrUnordered: - case JumpIfFloatLessOrEqualOrUnordered: - case JumpIfFloatGreaterOrEqualOrUnordered: - *thunk = true; - break; - - default: - break; - } - } - - virtual void planDestination - (TernaryOperation op, - unsigned, uint8_t, uint64_t, - unsigned, uint8_t, const uint64_t, - unsigned, uint8_t* cTypeMask, uint64_t* cRegisterMask) - { - if (isBranch(op)) { - *cTypeMask = (1 << ConstantOperand); - *cRegisterMask = 0; - } else { - *cTypeMask = (1 << RegisterOperand); - *cRegisterMask = ~static_cast(0); - } - } - - virtual void acquire() { - ++ referenceCount; - } - - virtual void release() { - if (-- referenceCount == 0) { - c.s->free(this); - } - } - - ArchitectureContext c; - unsigned referenceCount; -}; - -class MyAssembler: public Assembler { - public: - MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): - c(s, a, zone), arch_(arch) - { } - - virtual void setClient(Client* client) { - assert(&c, c.client == 0); - c.client = client; - } - - virtual Architecture* arch() { - return arch_; - } - - virtual void checkStackOverflow(uintptr_t handler, - unsigned stackLimitOffsetFromThread) - { - Register stack(StackRegister); - Memory stackLimit(ThreadRegister, stackLimitOffsetFromThread); - Constant handlerConstant - (new(c.zone) ResolvedPromise(handler)); - branchRM(&c, JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, - &handlerConstant); - } - - virtual void saveFrame(unsigned stackOffset, unsigned) { - Register returnAddress(0); - emit(&c, mflr(returnAddress.low)); - - Memory returnAddressDst - (StackRegister, ReturnAddressOffset * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, - &returnAddressDst); - - Register stack(StackRegister); - Memory stackDst(ThreadRegister, stackOffset); - moveRM(&c, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); - } - - virtual void pushFrame(unsigned argumentCount, ...) { - struct { - unsigned size; - OperandType type; - Operand* operand; - } arguments[argumentCount]; - - va_list a; va_start(a, argumentCount); - unsigned footprint = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - arguments[i].size = va_arg(a, unsigned); - arguments[i].type = static_cast(va_arg(a, int)); - arguments[i].operand = va_arg(a, Operand*); - footprint += ceiling(arguments[i].size, TargetBytesPerWord); - } - va_end(a); - - allocateFrame(arch_->alignFrameSize(footprint)); - - unsigned offset = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - if (i < arch_->argumentRegisterCount()) { - Register dst(arch_->argumentRegister(i)); - - apply(Move, - arguments[i].size, arguments[i].type, arguments[i].operand, - pad(arguments[i].size, TargetBytesPerWord), RegisterOperand, - &dst); - - offset += ceiling(arguments[i].size, TargetBytesPerWord); - } else { - Memory dst - (ThreadRegister, (offset + FrameFooterSize) * TargetBytesPerWord); - - apply(Move, - arguments[i].size, arguments[i].type, arguments[i].operand, - pad(arguments[i].size, TargetBytesPerWord), MemoryOperand, &dst); - - offset += ceiling(arguments[i].size, TargetBytesPerWord); - } - } - } - - virtual void allocateFrame(unsigned footprint) { - Register returnAddress(0); - emit(&c, mflr(returnAddress.low)); - - Memory returnAddressDst - (StackRegister, ReturnAddressOffset * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &returnAddress, TargetBytesPerWord, - &returnAddressDst); - - Register stack(StackRegister); - Memory stackDst(StackRegister, -footprint * TargetBytesPerWord); - moveAndUpdateRM - (&c, TargetBytesPerWord, &stack, TargetBytesPerWord, &stackDst); - } - - virtual void adjustFrame(unsigned difference) { - Register nextStack(0); - Memory stackSrc(StackRegister, 0); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &nextStack); - - Memory stackDst(StackRegister, -difference * TargetBytesPerWord); - moveAndUpdateRM - (&c, TargetBytesPerWord, &nextStack, TargetBytesPerWord, &stackDst); - } - - virtual void popFrame(unsigned) { - Register stack(StackRegister); - Memory stackSrc(StackRegister, 0); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack); - - Register returnAddress(0); - Memory returnAddressSrc - (StackRegister, ReturnAddressOffset * TargetBytesPerWord); - moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, - &returnAddress); - - emit(&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, (ReturnAddressOffset + footprint) - * TargetBytesPerWord); - moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, - &tmp); - - emit(&c, mtlr(tmp.low)); - - Memory stackSrc(StackRegister, footprint * TargetBytesPerWord); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp); - - Memory stackDst - (StackRegister, (footprint - offset) * TargetBytesPerWord); - moveAndUpdateRM - (&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, &stackDst); - - if (returnAddressSurrogate != NoRegister) { - assert(&c, offset > 0); - - Register ras(returnAddressSurrogate); - Memory dst - (StackRegister, (ReturnAddressOffset + offset) - * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); - } - - if (framePointerSurrogate != NoRegister) { - assert(&c, offset > 0); - - Register fps(framePointerSurrogate); - Memory dst(StackRegister, offset * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst); - } - } else { - popFrame(footprint); - } - } else { - abort(&c); - } - } - - virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, - unsigned argumentFootprint) - { - popFrame(frameFootprint); - - assert(&c, argumentFootprint >= StackAlignmentInWords); - assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); - - if (TailCalls and argumentFootprint > StackAlignmentInWords) { - Register tmp(0); - Memory stackSrc(StackRegister, 0); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp); - - Memory stackDst(StackRegister, - (argumentFootprint - StackAlignmentInWords) - * TargetBytesPerWord); - moveAndUpdateRM - (&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, &stackDst); - } - - return_(&c); - } - - virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, - unsigned stackOffsetFromThread) - { - popFrame(frameFootprint); - - Register tmp1(0); - Memory stackSrc(StackRegister, 0); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &tmp1); - - Register tmp2(5); - Memory newStackSrc(ThreadRegister, stackOffsetFromThread); - moveMR(&c, TargetBytesPerWord, &newStackSrc, TargetBytesPerWord, &tmp2); - - Register stack(StackRegister); - subR(&c, TargetBytesPerWord, &stack, &tmp2, &tmp2); - - Memory stackDst(StackRegister, 0, tmp2.low); - moveAndUpdateRM - (&c, TargetBytesPerWord, &tmp1, TargetBytesPerWord, &stackDst); - - return_(&c); - } - - virtual void apply(Operation op) { - arch_->c.operations[op](&c); - } - - virtual void apply(UnaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand) - { - arch_->c.unaryOperations[index(&(arch_->c), op, aType)] - (&c, aSize, aOperand); - } - - virtual void apply(BinaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand) - { - arch_->c.binaryOperations[index(&(arch_->c), op, aType, bType)] - (&c, aSize, aOperand, bSize, bOperand); - } - - virtual void apply(TernaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType UNUSED, - Operand* bOperand, - unsigned cSize UNUSED, OperandType cType UNUSED, - Operand* cOperand) - { - if (isBranch(op)) { - assert(&c, aSize == bSize); - assert(&c, cSize == TargetBytesPerWord); - assert(&c, cType == ConstantOperand); - - arch_->c.branchOperations[branchIndex(&(arch_->c), aType, bType)] - (&c, op, aSize, aOperand, bOperand, cOperand); - } else { - assert(&c, bSize == cSize); - assert(&c, bType == RegisterOperand); - assert(&c, cType == RegisterOperand); - - arch_->c.ternaryOperations[index(&(arch_->c), op, aType)] - (&c, bSize, aOperand, bOperand, cOperand); - } - } - - virtual void setDestination(uint8_t* dst) { - c.result = dst; - } - - virtual void write() { - uint8_t* dst = c.result; - unsigned dstOffset = 0; - for (MyBlock* b = c.firstBlock; b; b = b->next) { - if (DebugJumps) { - fprintf(stderr, "write block %p\n", b); - } - - unsigned blockOffset = 0; - for (JumpEvent* e = b->jumpEventHead; e; e = e->next) { - unsigned size = e->offset - blockOffset; - memcpy(dst + dstOffset, c.code.data + b->offset + blockOffset, size); - blockOffset = e->offset; - dstOffset += size; - - unsigned jumpTableSize = 0; - for (JumpOffset* o = e->jumpOffsetHead; o; o = o->next) { - if (DebugJumps) { - fprintf(stderr, "visit offset %p %d in block %p\n", - o, o->offset, b); - } - - uint8_t* address = dst + dstOffset + jumpTableSize; - - if (needJump(b)) { - address += TargetBytesPerWord; - } - - o->task->jumpAddress = address; - - jumpTableSize += TargetBytesPerWord; - } - - assert(&c, jumpTableSize); - - bool jump = needJump(b); - if (jump) { - write4(dst + dstOffset, ::b(jumpTableSize + TargetBytesPerWord)); - } - - dstOffset += jumpTableSize + (jump ? TargetBytesPerWord : 0); - } - - unsigned size = b->size - blockOffset; - - memcpy(dst + dstOffset, - c.code.data + b->offset + blockOffset, - size); - - dstOffset += size; - } - - unsigned index = dstOffset; - assert(&c, index % TargetBytesPerWord == 0); - for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { - e->address = dst + index; - index += TargetBytesPerWord; - } - - for (Task* t = c.tasks; t; t = t->next) { - t->run(&c); - } - - for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { - *static_cast(e->address) = e->constant->value(); -// fprintf(stderr, "constant %p at %p\n", reinterpret_cast(e->constant->value()), e->address); - } - } - - virtual Promise* offset(bool) { - return ::offset(&c); - } - - virtual Block* endBlock(bool startNew) { - MyBlock* b = c.lastBlock; - b->size = c.code.length() - b->offset; - if (startNew) { - c.lastBlock = new(c.zone) MyBlock(&c, c.code.length()); - } else { - c.lastBlock = 0; - } - return b; - } - - virtual void endEvent() { - MyBlock* b = c.lastBlock; - unsigned thisEventOffset = c.code.length() - b->offset; - if (b->jumpOffsetHead) { - int32_t v = (thisEventOffset + TargetBytesPerWord) - - b->jumpOffsetHead->offset; - - if (v > 0 and not bounded(2, 16, v)) { - appendJumpEvent - (&c, b, b->lastEventOffset, b->jumpOffsetHead, - b->lastJumpOffsetTail); - - if (DebugJumps) { - for (JumpOffset* o = b->jumpOffsetHead; - o != b->lastJumpOffsetTail->next; o = o->next) - { - fprintf(stderr, - "in endEvent, include %p %d in jump event %p " - "at offset %d in block %p\n", - o, o->offset, b->jumpEventTail, b->lastEventOffset, b); - } - } - - b->jumpOffsetHead = b->lastJumpOffsetTail->next; - b->lastJumpOffsetTail->next = 0; - if (b->jumpOffsetHead == 0) { - b->jumpOffsetTail = 0; - } - } - } - b->lastEventOffset = thisEventOffset; - b->lastJumpOffsetTail = b->jumpOffsetTail; - } - - virtual unsigned length() { - return c.code.length(); - } - - virtual unsigned footerSize() { - return c.constantPoolCount * TargetBytesPerWord; - } - - virtual void dispose() { - c.code.dispose(); - } - - Context c; - MyArchitecture* arch_; -}; - -} // namespace - -namespace vm { - -Assembler::Architecture* -makeArchitecture(System* system, bool) -{ - return new (allocate(system, sizeof(MyArchitecture))) MyArchitecture(system); -} - -Assembler* -makeAssembler(System* system, Allocator* allocator, Zone* zone, - Assembler::Architecture* architecture) -{ - return - new(zone) MyAssembler(system, allocator, zone, - static_cast(architecture)); -} - -} // namespace vm diff --git a/src/process.cpp b/src/process.cpp index ac1c21917e..f2630a0a6f 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -8,7 +8,9 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "process.h" +#include "avian/process.h" + +#include using namespace vm; diff --git a/src/tokenizer.h b/src/tokenizer.h deleted file mode 100644 index 23b7788232..0000000000 --- a/src/tokenizer.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) 2010-2011, 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. */ - -#ifndef TOKENIZER_H -#define TOKENIZER_H - -namespace vm { - -class Tokenizer { - public: - class Token { - public: - Token(const char* s, unsigned length): s(s), length(length) { } - - const char* s; - unsigned length; - }; - - Tokenizer(const char* s, char delimiter): - s(s), limit(0), delimiter(delimiter) - { } - - Tokenizer(const char* s, unsigned length, char delimiter): - s(s), limit(s + length), delimiter(delimiter) - { } - - bool hasMore() { - while (*s == delimiter and s != limit) ++s; - return *s != 0 and s != limit; - } - - Token next() { - const char* p = s; - while (*s and *s != delimiter and s != limit) ++s; - return Token(p, s - p); - } - - const char* s; - const char* limit; - char delimiter; -}; - -} // namespace - -#endif//TOKENIZER_H diff --git a/src/tools/audit-codegen/main.cpp b/src/tools/audit-codegen/main.cpp new file mode 100644 index 0000000000..5cc7d79709 --- /dev/null +++ b/src/tools/audit-codegen/main.cpp @@ -0,0 +1,116 @@ +/* Copyright (c) 2008-2012, 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 + +#include + +#include +#include +#include +#include + +#include + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) { abort(); } + +using namespace vm; +using namespace avian::codegen; +using namespace avian::util; + +class BasicEnv { +public: + System* s; + Heap* heap; + Architecture* arch; + + BasicEnv(): + s(makeSystem(0)), + heap(makeHeap(s, 32 * 1024)), + arch(makeArchitectureNative(s, true)) + { + arch->acquire(); + } + + ~BasicEnv() { + arch->release(); + s->dispose(); + } +}; + +class Asm { +public: + Zone zone; + Assembler* a; + + Asm(BasicEnv& env): + zone(env.s, env.heap, 8192), + a(env.arch->makeAssembler(env.heap, &zone)) + { } + + ~Asm() { + a->dispose(); + } +}; + +void generateCode(BasicEnv& env) { + Asm a(env); + for(RegisterIterator it(env.arch->registerFile()->generalRegisters); it.hasNext(); ) { + int r = it.next(); + lir::Register reg(r); + a.a->apply(lir::Add, + OperandInfo(4, lir::RegisterOperand, ®), + OperandInfo(4, lir::RegisterOperand, ®), + OperandInfo(4, lir::RegisterOperand, ®)); + } + unsigned length = a.a->endBlock(false)->resolve(0, 0); + printf("length: %d\n", length); + uint8_t* data = static_cast(env.s->tryAllocate(length)); + a.a->setDestination(data); + a.a->write(); + for(unsigned i = 0; i < length; i++) { + printf("%02x ", data[i]); + } + printf("\n"); + env.s->free(data); +} + +class Arguments { +public: + const char* output; + const char* outputFormat; + + Arguments(int argc, char** argv) { + ArgParser parser; + Arg out(parser, true, "output", ""); + Arg format(parser, true, "format", ""); + + if(!parser.parse(argc, argv)) { + exit(1); + } + + output = out.value; + outputFormat = format.value; + + // TODO: sanitize format values + } +}; + +int main(int argc, char** argv) { + Arguments args(argc, argv); + + BasicEnv env; + + generateCode(env); + + return 0; +} \ No newline at end of file diff --git a/src/binaryToObject/main.cpp b/src/tools/binary-to-object/main.cpp similarity index 98% rename from src/binaryToObject/main.cpp rename to src/tools/binary-to-object/main.cpp index fe389f3d29..86d780bbec 100644 --- a/src/binaryToObject/main.cpp +++ b/src/tools/binary-to-object/main.cpp @@ -22,7 +22,7 @@ #endif #include -#include "tools.h" +#include extern "C" void __cxa_pure_virtual() { diff --git a/src/bootimage.cpp b/src/tools/bootimage-generator/main.cpp similarity index 91% rename from src/bootimage.cpp rename to src/tools/bootimage-generator/main.cpp index 4525b3ec05..eac0b17de8 100644 --- a/src/bootimage.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -8,16 +8,21 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "heap.h" -#include "heapwalk.h" -#include "common.h" -#include "machine.h" -#include "util.h" -#include "stream.h" -#include "assembler.h" -#include "target.h" -#include "binaryToObject/tools.h" -#include "lzma.h" +#include +#include "avian/heapwalk.h" +#include "avian/common.h" +#include "avian/machine.h" +#include "avian/util.h" +#include +#include +#include +#include "avian/target.h" +#include +#include +#include "avian/lzma.h" + +#include +#include // since we aren't linking against libstdc++, we must implement this // ourselves: @@ -25,13 +30,15 @@ extern "C" void __cxa_pure_virtual(void) { abort(); } using namespace vm; using namespace avian::tools; +using namespace avian::util; +using namespace avian::codegen; namespace { const unsigned HeapCapacity = 512 * 1024 * 1024; const unsigned TargetFixieSizeInBytes = 8 + (TargetBytesPerWord * 2); -const unsigned TargetFixieSizeInWords = ceiling +const unsigned TargetFixieSizeInWords = ceilingDivide (TargetFixieSizeInBytes, TargetBytesPerWord); const unsigned TargetFixieAge = 0; const unsigned TargetFixieFlags = 2; @@ -278,6 +285,8 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, { PROTECT(t, typeMaps); + t->m->classpath->interceptMethods(t); + object constants = 0; PROTECT(t, constants); @@ -310,7 +319,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, if (endsWith(".class", name, nameSize) and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) { - // fprintf(stderr, "%.*s\n", nameSize - 6, name); + // fprintf(stderr, "pass 1 %.*s\n", nameSize - 6, name); object c = resolveSystemClass (t, root(t, Machine::BootLoader), makeByteArray(t, "%.*s", nameSize - 6, name), true); @@ -326,7 +335,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, Client(Thread* t): t(t) { } virtual void NO_RETURN handleError() { - vm::abort(t); + abort(t); } private: @@ -342,21 +351,21 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, unsigned count = s.read2() - 1; if (count) { - Type types[count + 2]; - types[0] = Type_object; - types[1] = Type_intptr_t; + THREAD_RUNTIME_ARRAY(t, Type, types, count + 2); + RUNTIME_ARRAY_BODY(types)[0] = Type_object; + RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; for (unsigned i = 2; i < count + 2; ++i) { switch (s.read1()) { case CONSTANT_Class: case CONSTANT_String: - types[i] = Type_object; + RUNTIME_ARRAY_BODY(types)[i] = Type_object; s.skip(2); break; case CONSTANT_Integer: case CONSTANT_Float: - types[i] = Type_int32_t; + RUNTIME_ARRAY_BODY(types)[i] = Type_int32_t; s.skip(4); break; @@ -364,24 +373,24 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: - types[i] = Type_object; + RUNTIME_ARRAY_BODY(types)[i] = Type_object; s.skip(4); break; case CONSTANT_Long: - types[i++] = Type_int64_t; - types[i] = Type_int64_t_pad; + RUNTIME_ARRAY_BODY(types)[i++] = Type_int64_t; + RUNTIME_ARRAY_BODY(types)[i] = Type_int64_t_pad; s.skip(8); break; case CONSTANT_Double: - types[i++] = Type_double; - types[i] = Type_double_pad; + RUNTIME_ARRAY_BODY(types)[i++] = Type_double; + RUNTIME_ARRAY_BODY(types)[i] = Type_double_pad; s.skip(8); break; case CONSTANT_Utf8: - types[i] = Type_object; + RUNTIME_ARRAY_BODY(types)[i] = Type_object; s.skip(s.read2()); break; @@ -401,7 +410,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, map->targetFixedOffsets()[i * BytesPerWord] = i * TargetBytesPerWord; - init(new (map->fixedFields() + i) Field, types[i], + init(new (map->fixedFields() + i) Field, RUNTIME_ARRAY_BODY(types)[i], i * BytesPerWord, BytesPerWord, i * TargetBytesPerWord, TargetBytesPerWord); } @@ -412,8 +421,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, objectHash); } } - - // if (strcmp(name, "java/lang/System$Property.class") == 0) trap(); { object array = 0; PROTECT(t, array); @@ -422,7 +429,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, object fields = allFields(t, typeMaps, c, &count, &array); PROTECT(t, fields); - Field memberFields[count + 1]; + THREAD_RUNTIME_ARRAY(t, Field, memberFields, count + 1); unsigned memberIndex; unsigned buildMemberOffset; @@ -439,32 +446,39 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, for (unsigned j = 0; j < map->fixedFieldCount; ++j) { Field* f = map->fixedFields() + j; - memberFields[memberIndex] = *f; + RUNTIME_ARRAY_BODY(memberFields)[memberIndex] = *f; targetMemberOffset = f->targetOffset + f->targetSize; ++ memberIndex; } } else { - init(new (memberFields) Field, Type_object, 0, BytesPerWord, 0, - TargetBytesPerWord); + init(new (RUNTIME_ARRAY_BODY(memberFields)) Field, Type_object, 0, + BytesPerWord, 0, TargetBytesPerWord); memberIndex = 1; buildMemberOffset = BytesPerWord; targetMemberOffset = TargetBytesPerWord; } - Field staticFields[count + 2]; + const unsigned StaticHeader = 3; + + THREAD_RUNTIME_ARRAY(t, Field, staticFields, count + StaticHeader); - init(new (staticFields) Field, Type_object, 0, BytesPerWord, 0, + init(new (RUNTIME_ARRAY_BODY(staticFields)) Field, Type_object, 0, + BytesPerWord, 0, TargetBytesPerWord); + + init(new (RUNTIME_ARRAY_BODY(staticFields) + 1) Field, Type_intptr_t, + BytesPerWord, BytesPerWord, TargetBytesPerWord, TargetBytesPerWord); - init(new (staticFields + 1) Field, Type_intptr_t, BytesPerWord, - BytesPerWord, TargetBytesPerWord, TargetBytesPerWord); + init(new (RUNTIME_ARRAY_BODY(staticFields) + 2) Field, Type_object, + BytesPerWord * 2, BytesPerWord, TargetBytesPerWord * 2, + TargetBytesPerWord); - unsigned staticIndex = 2; - unsigned buildStaticOffset = BytesPerWord * 2; - unsigned targetStaticOffset = TargetBytesPerWord * 2; + unsigned staticIndex = StaticHeader; + unsigned buildStaticOffset = BytesPerWord * StaticHeader; + unsigned targetStaticOffset = TargetBytesPerWord * StaticHeader; for (unsigned i = 0; i < vectorSize(t, fields); ++i) { object field = vectorBody(t, fields, i); @@ -509,8 +523,8 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, buildStaticOffset = fieldOffset(t, field); - init(new (staticFields + staticIndex) Field, type, - buildStaticOffset, buildSize, targetStaticOffset, + init(new (RUNTIME_ARRAY_BODY(staticFields) + staticIndex) Field, + type, buildStaticOffset, buildSize, targetStaticOffset, targetSize); targetStaticOffset += targetSize; @@ -523,8 +537,8 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, buildMemberOffset = fieldOffset(t, field); - init(new (memberFields + memberIndex) Field, type, - buildMemberOffset, buildSize, targetMemberOffset, + init(new (RUNTIME_ARRAY_BODY(memberFields) + memberIndex) Field, + type, buildMemberOffset, buildSize, targetMemberOffset, targetSize); targetMemberOffset += targetSize; @@ -535,18 +549,18 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, targetMemberOffset = pad(targetMemberOffset, TargetBytesPerWord); } } - + if (hashMapFind(t, typeMaps, c, objectHash, objectEqual) == 0) { object array = makeByteArray (t, TypeMap::sizeInBytes - (ceiling(classFixedSize(t, c), BytesPerWord), memberIndex)); + (ceilingDivide(classFixedSize(t, c), BytesPerWord), memberIndex)); TypeMap* map = new (&byteArrayBody(t, array, 0)) TypeMap - (ceiling(classFixedSize(t, c), BytesPerWord), - ceiling(targetMemberOffset, TargetBytesPerWord), memberIndex); + (ceilingDivide(classFixedSize(t, c), BytesPerWord), + ceilingDivide(targetMemberOffset, TargetBytesPerWord), memberIndex); for (unsigned i = 0; i < memberIndex; ++i) { - Field* f = memberFields + i; + Field* f = RUNTIME_ARRAY_BODY(memberFields) + i; expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); @@ -566,11 +580,11 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, TypeMap* map = new (&byteArrayBody(t, array, 0)) TypeMap (singletonCount(t, classStaticTable(t, c)) + 2, - ceiling(targetStaticOffset, TargetBytesPerWord), staticIndex, + ceilingDivide(targetStaticOffset, TargetBytesPerWord), staticIndex, TypeMap::SingletonKind); for (unsigned i = 0; i < staticIndex; ++i) { - Field* f = staticFields + i; + Field* f = RUNTIME_ARRAY_BODY(staticFields) + i; expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); @@ -594,7 +608,7 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, if (endsWith(".class", name, nameSize) and (className == 0 or strncmp(name, className, nameSize - 6) == 0)) { - // fprintf(stderr, "%.*s\n", nameSize - 6, name); + // fprintf(stderr, "pass 2 %.*s\n", nameSize - 6, name); object c = resolveSystemClass (t, root(t, Machine::BootLoader), makeByteArray(t, "%.*s", nameSize - 6, name), true); @@ -736,8 +750,8 @@ targetSize(Thread* t, object typeMaps, object p) if (map->targetArrayElementSizeInBytes) { return map->targetFixedSizeInWords - + ceiling(map->targetArrayElementSizeInBytes - * cast + + ceilingDivide(map->targetArrayElementSizeInBytes + * fieldAtOffset (p, (map->buildFixedSizeInWords - 1) * BytesPerWord), TargetBytesPerWord); } else { @@ -784,7 +798,7 @@ targetSize(Thread* t, object typeMaps, object referer, unsigned refererOffset, { return (TargetBytesPerWord * 2) + pad - (ceiling + (ceilingDivide (objectMaskCount (classTypeMap(t, typeMaps, referer)), 32) * 4, TargetBytesPerWord); } else { @@ -885,7 +899,7 @@ nonObjectsEqual(TypeMap* map, uint8_t* src, uint8_t* dst) if (map->targetArrayElementSizeInBytes) { unsigned fixedSize = map->buildFixedSizeInWords * BytesPerWord; - unsigned count = cast(src, fixedSize - BytesPerWord); + unsigned count = fieldAtOffset(src, fixedSize - BytesPerWord); for (unsigned i = 0; i < count; ++i) { if (not nonObjectsEqual @@ -916,7 +930,7 @@ copy(Thread* t, object typeMaps, object p, uint8_t* dst) if (map->targetArrayElementSizeInBytes) { unsigned fixedSize = map->buildFixedSizeInWords * BytesPerWord; - unsigned count = cast(p, fixedSize - BytesPerWord); + unsigned count = fieldAtOffset(p, fixedSize - BytesPerWord); for (unsigned i = 0; i < count; ++i) { copy(t, src + fixedSize + (i * map->buildArrayElementSizeInBytes), @@ -1066,7 +1080,7 @@ copy(Thread* t, object typeMaps, object referer, unsigned refererOffset, memset(dst, 0, TargetBytesPerWord); - unsigned length = ceiling(objectMaskCount(map), 32); + unsigned length = ceilingDivide(objectMaskCount(map), 32); target_uintptr_t targetLength = targetVW(length); @@ -1161,7 +1175,7 @@ makeHeapImage(Thread* t, BootImage* image, target_uintptr_t* heap, target_uintptr_t* dst = heap + position + TargetFixieSizeInWords; - unsigned maskSize = ceiling(size, TargetBitsPerWord); + unsigned maskSize = ceilingDivide(size, TargetBitsPerWord); unsigned total = TargetFixieSizeInWords + size + maskSize; @@ -1331,10 +1345,10 @@ writeBootImage2(Thread* t, OutputStream* bootimageOutput, OutputStream* codeOutp } ++ count; - Field fields[count]; + THREAD_RUNTIME_ARRAY(t, Field, fields, count); - init(new (fields) Field, Type_object, 0, BytesPerWord, 0, - TargetBytesPerWord); + init(new (RUNTIME_ARRAY_BODY(fields)) Field, Type_object, 0, + BytesPerWord, 0, TargetBytesPerWord); unsigned buildOffset = BytesPerWord; unsigned targetOffset = TargetBytesPerWord; @@ -1411,8 +1425,8 @@ writeBootImage2(Thread* t, OutputStream* bootimageOutput, OutputStream* codeOutp ++ targetOffset; } - init(new (fields + j) Field, type, buildOffset, buildSize, - targetOffset, targetSize); + init(new (RUNTIME_ARRAY_BODY(fields) + j) Field, type, buildOffset, + buildSize, targetOffset, targetSize); buildOffset += buildSize; targetOffset += targetSize; @@ -1437,16 +1451,16 @@ writeBootImage2(Thread* t, OutputStream* bootimageOutput, OutputStream* codeOutp object array = makeByteArray (t, TypeMap::sizeInBytes - (ceiling(buildOffset, BytesPerWord), fixedFieldCount)); + (ceilingDivide(buildOffset, BytesPerWord), fixedFieldCount)); TypeMap* map = new (&byteArrayBody(t, array, 0)) TypeMap - (ceiling(buildOffset, BytesPerWord), - ceiling(targetOffset, TargetBytesPerWord), + (ceilingDivide(buildOffset, BytesPerWord), + ceilingDivide(targetOffset, TargetBytesPerWord), fixedFieldCount, TypeMap::NormalKind, buildArrayElementSize, targetArrayElementSize, arrayElementType); for (unsigned j = 0; j < fixedFieldCount; ++j) { - Field* f = fields + j; + Field* f = RUNTIME_ARRAY_BODY(fields) + j; expect(t, f->buildOffset < map->buildFixedSizeInWords * BytesPerWord); @@ -1643,10 +1657,10 @@ writeBootImage2(Thread* t, OutputStream* bootimageOutput, OutputStream* codeOutp Platform* platform = Platform::getPlatform(PlatformInfo((PlatformInfo::Format)AVIAN_TARGET_FORMAT, (PlatformInfo::Architecture)AVIAN_TARGET_ARCH)); - // if(!platform) { - // fprintf(stderr, "unsupported platform: %s/%s\n", os, architecture); - // return false; - // } + if(!platform) { + fprintf(stderr, "unsupported platform: target-format = %d / target-arch = %d\n", AVIAN_TARGET_FORMAT, AVIAN_TARGET_ARCH); + abort(); + } SymbolInfo bootimageSymbols[] = { SymbolInfo(0, bootimageStart), @@ -1711,105 +1725,6 @@ writeBootImage(Thread* t, uintptr_t* arguments) return 1; } -class Arg; - -class ArgParser { -public: - Arg* first; - Arg** last; - - ArgParser(): - first(0), - last(&first) {} - - bool parse(int ac, const char** av); - void printUsage(const char* exe); -}; - -class Arg { -public: - Arg* next; - bool required; - const char* name; - const char* desc; - - const char* value; - - Arg(ArgParser& parser, bool required, const char* name, const char* desc): - next(0), - required(required), - name(name), - desc(desc), - value(0) - { - *parser.last = this; - parser.last = &next; - } -}; - -bool ArgParser::parse(int ac, const char** av) { - Arg* state = 0; - - for(int i = 1; i < ac; i++) { - if(state) { - if(state->value) { - fprintf(stderr, "duplicate parameter %s: '%s' and '%s'\n", state->name, state->value, av[i]); - return false; - } - state->value = av[i]; - state = 0; - } else { - if(av[i][0] != '-') { - fprintf(stderr, "expected -parameter\n"); - return false; - } - bool found = false; - for(Arg* arg = first; arg; arg = arg->next) { - if(strcmp(arg->name, &av[i][1]) == 0) { - found = true; - if (arg->desc == 0) { - arg->value = "true"; - } else { - state = arg; - } - } - } - if (not found) { - fprintf(stderr, "unrecognized parameter %s\n", av[i]); - return false; - } - } - } - - if(state) { - fprintf(stderr, "expected argument after -%s\n", state->name); - return false; - } - - for(Arg* arg = first; arg; arg = arg->next) { - if(arg->required && !arg->value) { - fprintf(stderr, "expected value for %s\n", arg->name); - return false; - } - } - - return true; -} - -void ArgParser::printUsage(const char* exe) { - fprintf(stderr, "usage:\n%s \\\n", exe); - for(Arg* arg = first; arg; arg = arg->next) { - const char* lineEnd = arg->next ? " \\" : ""; - if(arg->required) { - fprintf(stderr, " -%s\t%s%s\n", arg->name, arg->desc, lineEnd); - } else if (arg->desc) { - fprintf(stderr, " [-%s\t%s]%s\n", arg->name, arg->desc, lineEnd); - } else { - fprintf(stderr, " [-%s]%s\n", arg->name, lineEnd); - } - } -} - char* myStrndup(const char* src, unsigned length) { @@ -1988,7 +1903,8 @@ main(int ac, const char** av) // in a branch instruction for the target architecture (~32MB on // PowerPC and ARM). When that limitation is removed, we'll be able // to specify a capacity as large as we like here: -#if (defined ARCH_x86_64) || (defined ARCH_x86_32) +#if (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86_64) \ + || (AVIAN_TARGET_ARCH == AVIAN_ARCH_X86) const unsigned CodeCapacity = 128 * 1024 * 1024; #else const unsigned CodeCapacity = 30 * 1024 * 1024; diff --git a/src/binaryToObject/elf.cpp b/src/tools/object-writer/elf.cpp similarity index 99% rename from src/binaryToObject/elf.cpp rename to src/tools/object-writer/elf.cpp index a2277c3488..555d6fd9ca 100644 --- a/src/binaryToObject/elf.cpp +++ b/src/tools/object-writer/elf.cpp @@ -15,7 +15,7 @@ #include "endianness.h" -#include "tools.h" +#include #define EI_NIDENT 16 @@ -72,6 +72,7 @@ namespace { using namespace avian::tools; +using namespace avian::util; template struct ElfTypes { diff --git a/src/binaryToObject/endianness.h b/src/tools/object-writer/endianness.h similarity index 100% rename from src/binaryToObject/endianness.h rename to src/tools/object-writer/endianness.h diff --git a/src/binaryToObject/mach-o.cpp b/src/tools/object-writer/mach-o.cpp similarity index 99% rename from src/binaryToObject/mach-o.cpp rename to src/tools/object-writer/mach-o.cpp index 96dd63aafd..03066fc247 100644 --- a/src/binaryToObject/mach-o.cpp +++ b/src/tools/object-writer/mach-o.cpp @@ -14,7 +14,7 @@ #include "endianness.h" -#include "tools.h" +#include #define MH_MAGIC_64 0xfeedfacf #define MH_MAGIC 0xfeedface @@ -43,6 +43,7 @@ namespace { using namespace avian::tools; +using namespace avian::util; typedef int cpu_type_t; typedef int cpu_subtype_t; diff --git a/src/binaryToObject/pe.cpp b/src/tools/object-writer/pe.cpp similarity index 83% rename from src/binaryToObject/pe.cpp rename to src/tools/object-writer/pe.cpp index 8d5d5fc444..b96124582b 100644 --- a/src/binaryToObject/pe.cpp +++ b/src/tools/object-writer/pe.cpp @@ -13,17 +13,21 @@ #include #include -#include "tools.h" +#include namespace { -#define IMAGE_SIZEOF_SHORT_NAME 8 +// --- winnt.h ---- +#define IMAGE_SIZEOF_SHORT_NAME 8 -#define IMAGE_FILE_RELOCS_STRIPPED 1 -#define IMAGE_FILE_LINE_NUMS_STRIPPED 4 -#define IMAGE_FILE_MACHINE_AMD64 0x8664 -#define IMAGE_FILE_MACHINE_I386 0x014c -#define IMAGE_FILE_32BIT_MACHINE 256 +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file. +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file. +#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) +#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386. +#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian +#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian +#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine. #define IMAGE_SCN_ALIGN_1BYTES 0x100000 #define IMAGE_SCN_ALIGN_2BYTES 0x200000 @@ -73,6 +77,7 @@ struct IMAGE_SYMBOL { uint8_t StorageClass; uint8_t NumberOfAuxSymbols; } __attribute__((packed)); +// --- winnt.h ---- inline unsigned pad(unsigned n) @@ -81,8 +86,9 @@ pad(unsigned n) } using namespace avian::tools; +using namespace avian::util; -template +template class WindowsPlatform : public Platform { public: @@ -202,12 +208,15 @@ public: int machine; int machineMask; - if (BytesPerWord == 8) { + if (Architecture == PlatformInfo::x86_64) { machine = IMAGE_FILE_MACHINE_AMD64; machineMask = 0; - } else { // if (BytesPerWord == 8) + } else if (Architecture == PlatformInfo::x86) { machine = IMAGE_FILE_MACHINE_I386; machineMask = IMAGE_FILE_32BIT_MACHINE; + } else if (Architecture == PlatformInfo::Arm) { + machine = IMAGE_FILE_MACHINE_ARMNT; + machineMask = IMAGE_FILE_32BIT_MACHINE; } int sectionMask; @@ -269,10 +278,11 @@ public: } WindowsPlatform(): - Platform(PlatformInfo(PlatformInfo::Pe, BytesPerWord == 4 ? PlatformInfo::x86 : PlatformInfo::x86_64)) {} + Platform(PlatformInfo(PlatformInfo::Pe, Architecture)) {} }; -WindowsPlatform<4> windows32Platform; -WindowsPlatform<8> windows64Platform; +WindowsPlatform<4, PlatformInfo::x86> windows32Platform; +WindowsPlatform<8, PlatformInfo::x86_64> windows64Platform; +WindowsPlatform<4, PlatformInfo::Arm> windowsRtPlatform; // Windows Phone 8 and Windows RT } // namespace diff --git a/src/binaryToObject/tools.cpp b/src/tools/object-writer/tools.cpp similarity index 96% rename from src/binaryToObject/tools.cpp rename to src/tools/object-writer/tools.cpp index 9cc9059b93..4284778550 100644 --- a/src/binaryToObject/tools.cpp +++ b/src/tools/object-writer/tools.cpp @@ -13,16 +13,14 @@ #include #include -#include "tools.h" +#include + +using namespace avian::util; namespace avian { namespace tools { -String::String(const char* text): - text(text), - length(strlen(text)) {} - Buffer::Buffer(): capacity(100), length(0), diff --git a/src/tools/type-generator/io.h b/src/tools/type-generator/io.h new file mode 100644 index 0000000000..f8c7116636 --- /dev/null +++ b/src/tools/type-generator/io.h @@ -0,0 +1,149 @@ +/* Copyright (c) 2008-2011, 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 "assert.h" + +#ifndef AVIAN_TOOLS_TYPE_GENERATOR_IO_H +#define AVIAN_TOOLS_TYPE_GENERATOR_IO_H + +namespace avian { +namespace tools { +namespace typegenerator { + +class Input { + public: + virtual ~Input() { } + + virtual void dispose() = 0; + + virtual int peek() = 0; + + virtual int read() = 0; + + virtual unsigned line() = 0; + + virtual unsigned column() = 0; + + void skipSpace() { + bool quit = false; + while (not quit) { + int c = peek(); + switch (c) { + case ' ': case '\t': case '\n': + read(); + break; + + default: quit = true; + } + } + } +}; + +class FileInput : public Input { + public: + const char* file; + FILE* stream; + unsigned line_; + unsigned column_; + bool close; + + FileInput(const char* file, FILE* stream = 0, bool close = true): + file(file), stream(stream), line_(1), column_(1), close(close) + { } + + virtual ~FileInput() { + dispose(); + } + + virtual void dispose() { + if (stream and close) { + fclose(stream); + stream = 0; + } + } + + virtual int peek() { + int c = getc(stream); + ungetc(c, stream); + return c; + } + + virtual int read() { + int c = getc(stream); + if (c == '\n') { + ++ line_; + column_ = 1; + } else { + ++ column_; + } + return c; + } + + virtual unsigned line() { + return line_; + } + + virtual unsigned column() { + return column_; + } +}; + +class Output { + public: + virtual ~Output() { } + + virtual void dispose() = 0; + + virtual void write(const char* s) = 0; + + void write(int i) { + static const int Size = 32; + char s[Size]; + int c UNUSED = ::snprintf(s, Size, "%d", i); + assert(c > 0 and c < Size); + write(s); + } +}; + +class FileOutput : public Output { + public: + const char* file; + FILE* stream; + bool close; + + FileOutput(const char* file, FILE* stream = 0, bool close = true): + file(file), stream(stream), close(close) + { } + + virtual ~FileOutput() { + dispose(); + } + + virtual void dispose() { + if (stream and close) { + fclose(stream); + stream = 0; + } + } + + virtual void write(const char* s) { + fputs(s, stream); + } + + const char* filename() { + return file; + } +}; + +} // namespace typegenerator +} // namespace tools +} // namespace avian + +#endif // AVIAN_TOOLS_TYPE_GENERATOR_IO_H diff --git a/src/type-generator.cpp b/src/tools/type-generator/main.cpp similarity index 92% rename from src/type-generator.cpp rename to src/tools/type-generator/main.cpp index 290aec459a..3ac2ab3a24 100644 --- a/src/type-generator.cpp +++ b/src/tools/type-generator/main.cpp @@ -14,9 +14,12 @@ #include "string.h" #include "errno.h" -#include "constants.h" -#include "finder.h" -#include "stream.h" +#include "avian/constants.h" +#include "avian/finder.h" +#include + +#include "io.h" +#include "sexpr.h" #include "assert.h" @@ -29,6 +32,7 @@ inline void operator delete(void*) { abort(); } extern "C" void __cxa_pure_virtual(void) { abort(); } using namespace vm; +using namespace avian::tools::typegenerator; namespace { @@ -55,15 +59,6 @@ pad(unsigned n) return (extra ? n + BytesPerWord - extra : n); } -template -T* -allocate() -{ - T* t = static_cast(malloc(sizeof(T))); - assert(t); - return t; -} - inline bool equal(const char* a, const char* b) { @@ -88,214 +83,6 @@ take(unsigned n, const char* c) return r; } -class Input { - public: - virtual ~Input() { } - - virtual void dispose() = 0; - - virtual int peek() = 0; - - virtual int read() = 0; - - virtual unsigned line() = 0; - - virtual unsigned column() = 0; - - void skipSpace() { - bool quit = false; - while (not quit) { - int c = peek(); - switch (c) { - case ' ': case '\t': case '\n': - read(); - break; - - default: quit = true; - } - } - } -}; - -class FileInput : public Input { - public: - const char* file; - FILE* stream; - unsigned line_; - unsigned column_; - bool close; - - FileInput(const char* file, FILE* stream = 0, bool close = true): - file(file), stream(stream), line_(1), column_(1), close(close) - { } - - virtual ~FileInput() { - dispose(); - } - - virtual void dispose() { - if (stream and close) { - fclose(stream); - stream = 0; - } - } - - virtual int peek() { - int c = getc(stream); - ungetc(c, stream); - return c; - } - - virtual int read() { - int c = getc(stream); - if (c == '\n') { - ++ line_; - column_ = 1; - } else { - ++ column_; - } - return c; - } - - virtual unsigned line() { - return line_; - } - - virtual unsigned column() { - return column_; - } -}; - -class Output { - public: - virtual ~Output() { } - - virtual void dispose() = 0; - - virtual void write(const char* s) = 0; - - void write(int i) { - static const int Size = 32; - char s[Size]; - int c UNUSED = ::snprintf(s, Size, "%d", i); - assert(c > 0 and c < Size); - write(s); - } -}; - -class FileOutput : public Output { - public: - const char* file; - FILE* stream; - bool close; - - FileOutput(const char* file, FILE* stream = 0, bool close = true): - file(file), stream(stream), close(close) - { } - - virtual ~FileOutput() { - dispose(); - } - - virtual void dispose() { - if (stream and close) { - fclose(stream); - stream = 0; - } - } - - virtual void write(const char* s) { - fputs(s, stream); - } - - const char* filename() { - return file; - } -}; - -class Object { - public: - typedef enum { - Scalar, - Array, - Method, - Type, - Pair, - Number, - Character, - String, - Eos - } ObjectType; - - ObjectType type; -}; - -class Pair : public Object { - public: - Object* car; - Object* cdr; - - static Pair* make(Object* car, Object* cdr) { - Pair* o = allocate(); - o->type = Object::Pair; - o->car = car; - o->cdr = cdr; - return o; - } -}; - -Object* -cons(Object* car, Object* cdr) -{ - return Pair::make(car, cdr); -} - -Object*& -car(Object* o) -{ - assert(o->type == Object::Pair); - return static_cast(o)->car; -} - -void -setCar(Object* o, Object* v) -{ - assert(o->type == Object::Pair); - static_cast(o)->car = v; -} - -Object*& -cdr(Object* o) -{ - assert(o->type == Object::Pair); - return static_cast(o)->cdr; -} - -void -setCdr(Object* o, Object* v) -{ - assert(o->type == Object::Pair); - static_cast(o)->cdr = v; -} - -class List { - public: - Object* first; - Object* last; - - List(): first(0), last(0) { } - - void append(Object* o) { - Object* p = cons(o, 0); - if (last) { - setCdr(last, p); - last = p; - } else { - first = last = p; - } - } -}; - class Scalar : public Object { public: Object* owner; @@ -875,6 +662,7 @@ class MemberIterator { assert(member->type == Object::Scalar); offset_ = ((offset_ + size_) + (BytesPerWord - 1)) & ~(BytesPerWord - 1); + alignment_ = BytesPerWord; member = 0; } @@ -2176,9 +1964,9 @@ main(int ac, char** av) fprintf(stderr, "unable to open %s: %s\n", av[2], strerror(errno)); return -1; } - local::FileInput in(0, inStream, false); + FileInput in(0, inStream, false); - local::Object* declarations = local::parse(finder, &in); + Object* declarations = local::parse(finder, &in); finder->dispose(); system->dispose(); @@ -2188,7 +1976,7 @@ main(int ac, char** av) fprintf(stderr, "unable to open %s: %s\n", av[3], strerror(errno)); return -1; } - local::FileOutput out(0, outStream, false); + FileOutput out(0, outStream, false); if (local::equal(av[4], "enums")) { local::writeEnums(&out, declarations); diff --git a/src/tools/type-generator/sexpr.h b/src/tools/type-generator/sexpr.h new file mode 100644 index 0000000000..cc0b7dc750 --- /dev/null +++ b/src/tools/type-generator/sexpr.h @@ -0,0 +1,102 @@ +/* Copyright (c) 2008-2011, 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. */ + +#ifndef AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H +#define AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H + +namespace avian { +namespace tools { +namespace typegenerator { + +template +inline T* allocate() { + T* t = static_cast(malloc(sizeof(T))); + assert(t); + return t; +} + +class Object { + public: + typedef enum { + Scalar, + Array, + Method, + Type, + Pair, + Number, + Character, + String, + Eos + } ObjectType; + + ObjectType type; +}; + +class Pair : public Object { + public: + Object* car; + Object* cdr; + + static Pair* make(Object* car, Object* cdr) { + Pair* o = allocate(); + o->type = Object::Pair; + o->car = car; + o->cdr = cdr; + return o; + } +}; + +inline Object* cons(Object* car, Object* cdr) { + return Pair::make(car, cdr); +} + +inline Object*& car(Object* o) { + assert(o->type == Object::Pair); + return static_cast(o)->car; +} + +inline void setCar(Object* o, Object* v) { + assert(o->type == Object::Pair); + static_cast(o)->car = v; +} + +inline Object*& cdr(Object* o) { + assert(o->type == Object::Pair); + return static_cast(o)->cdr; +} + +inline void setCdr(Object* o, Object* v) { + assert(o->type == Object::Pair); + static_cast(o)->cdr = v; +} + +class List { + public: + Object* first; + Object* last; + + List(): first(0), last(0) { } + + void append(Object* o) { + Object* p = cons(o, 0); + if (last) { + setCdr(last, p); + last = p; + } else { + first = last = p; + } + } +}; + +} // namespace typegenerator +} // namespace tools +} // namespace avian + +#endif // AVIAN_TOOLS_TYPE_GENERATOR_SEXPR_H diff --git a/src/types.def b/src/types.def index 31d96404a8..c7731f648d 100644 --- a/src/types.def +++ b/src/types.def @@ -16,6 +16,10 @@ (type constantPool sun/reflect/ConstantPool) +(type serializable java/io/Serializable) + +(type cloneable java/lang/Cloneable) + (type singleton (array uintptr_t body)) @@ -188,7 +192,8 @@ (require object interruptLock) (require uint8_t interrupted) (require uint8_t unparked) - (alias peer uint64_t eetop)) + (alias peer uint64_t eetop) + (require uint64_t peer)) (type threadGroup java/lang/ThreadGroup) @@ -196,7 +201,8 @@ (type throwable java/lang/Throwable (alias message object detailMessage) - (alias trace object backtrace)) + (alias trace object backtrace) + (alias trace object stackState)) (type exception java/lang/Exception) @@ -220,6 +226,8 @@ (type negativeArraySizeException java/lang/NegativeArraySizeException) +(type cloneNotSupportedException java/lang/CloneNotSupportedException) + (type reflectiveOperationException java/lang/ReflectiveOperationException) (type classCastException java/lang/ClassCastException) @@ -286,7 +294,9 @@ (alias target object referent) (alias queue object queue) (alias jNext object next) + (alias jNext object queueNext) (alias vmNext object discovered) + (alias vmNext object pendingNext) (nogc object target) (nogc object queue) (nogc object vmNext)) diff --git a/src/util.cpp b/src/util.cpp index 915913e6e1..1421824919 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "util.h" +#include "avian/util.h" using namespace vm; @@ -449,6 +449,12 @@ hashMapInsert(Thread* t, object map, object key, object value, set(t, n, TripleThird, arrayBody(t, array, index)); set(t, array, ArrayBody + (index * BytesPerWord), n); + + if (hashMapSize(t, map) <= arrayLength(t, array) / 3) { + // this might happen if nodes were removed during GC in which case + // we weren't able to resize at the time + hashMapResize(t, map, hash, arrayLength(t, array) / 2); + } } object @@ -495,7 +501,9 @@ hashMapRemove(Thread* t, object map, object key, } } - if (hashMapSize(t, map) <= arrayLength(t, array) / 3) { + if ((not t->m->collecting) + and hashMapSize(t, map) <= arrayLength(t, array) / 3) + { PROTECT(t, o); hashMapResize(t, map, hash, arrayLength(t, array) / 2); } diff --git a/src/util/arg-parser.cpp b/src/util/arg-parser.cpp new file mode 100644 index 0000000000..1d87051819 --- /dev/null +++ b/src/util/arg-parser.cpp @@ -0,0 +1,99 @@ +/* Copyright (c) 2008-2012, 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 +#include + +#include +#include + +namespace avian { +namespace util { + +Arg::Arg(ArgParser& parser, bool required, const char* name, const char* desc): + next(0), + required(required), + name(name), + desc(desc), + value(0) +{ + *parser.last = this; + parser.last = &next; +} + +ArgParser::ArgParser(): + first(0), + last(&first) {} + +bool ArgParser::parse(int ac, const char* const* av) { + Arg* state = 0; + + for(int i = 1; i < ac; i++) { + if(state) { + if(state->value) { + fprintf(stderr, "duplicate parameter %s: '%s' and '%s'\n", state->name, state->value, av[i]); + return false; + } + state->value = av[i]; + state = 0; + } else { + if(av[i][0] != '-') { + fprintf(stderr, "expected -parameter\n"); + return false; + } + bool found = false; + for(Arg* arg = first; arg; arg = arg->next) { + if(strcmp(arg->name, &av[i][1]) == 0) { + found = true; + if (arg->desc == 0) { + arg->value = "true"; + } else { + state = arg; + } + } + } + if (not found) { + fprintf(stderr, "unrecognized parameter %s\n", av[i]); + return false; + } + } + } + + if(state) { + fprintf(stderr, "expected argument after -%s\n", state->name); + return false; + } + + for(Arg* arg = first; arg; arg = arg->next) { + if(arg->required && !arg->value) { + fprintf(stderr, "expected value for %s\n", arg->name); + return false; + } + } + + return true; +} + +void ArgParser::printUsage(const char* exe) { + fprintf(stderr, "usage:\n%s \\\n", exe); + for(Arg* arg = first; arg; arg = arg->next) { + const char* lineEnd = arg->next ? " \\" : ""; + if(arg->required) { + fprintf(stderr, " -%s\t%s%s\n", arg->name, arg->desc, lineEnd); + } else if (arg->desc) { + fprintf(stderr, " [-%s\t%s]%s\n", arg->name, arg->desc, lineEnd); + } else { + fprintf(stderr, " [-%s]%s\n", arg->name, lineEnd); + } + } +} + +} // namespace util +} // namespace avian diff --git a/src/posix.cpp b/src/vm/system/posix.cpp similarity index 97% rename from src/posix.cpp rename to src/vm/system/posix.cpp index 1394a26886..81d9c39246 100644 --- a/src/posix.cpp +++ b/src/vm/system/posix.cpp @@ -12,10 +12,21 @@ # define __STDC_CONSTANT_MACROS #endif +#include "sys/types.h" #ifdef __APPLE__ # include "CoreFoundation/CoreFoundation.h" # include "sys/ucontext.h" # undef assert +#elif defined(__ANDROID__) +# include /* for sigcontext */ +# include /* for stack_t */ + typedef struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + unsigned long uc_sigmask; + } ucontext_t; #else # if defined __FreeBSD__ # include "limits.h" @@ -24,7 +35,7 @@ #endif #include "sys/mman.h" -#include "sys/types.h" + #include "sys/stat.h" #include "sys/time.h" #include "time.h" @@ -37,13 +48,16 @@ #include "stdint.h" #include "dirent.h" #include "sched.h" +#include "avian/arch.h" +#include + +#include -#include "arch.h" -#include "system.h" #define ACQUIRE(x) MutexResource MAKE_NAME(mutexResource_) (x) using namespace vm; +using namespace avian::util; namespace { @@ -173,6 +187,8 @@ class MySystem: public System { } virtual void dispose() { + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&condition); ::free(this); } @@ -814,7 +830,7 @@ class MySystem: public System { // been unable to track it down so far. The workaround is to give // it 8 words more than it should need, where 8 is a number I just // made up and seems to work. - void* array[ceiling(sizeof(struct stat), sizeof(void*)) + 8]; + void* array[ceilingDivide(sizeof(struct stat), sizeof(void*)) + 8]; struct stat* s = reinterpret_cast(array); #endif diff --git a/src/windows.cpp b/src/vm/system/windows.cpp similarity index 78% rename from src/windows.cpp rename to src/vm/system/windows.cpp index 532b66e161..9f9de4816e 100644 --- a/src/windows.cpp +++ b/src/vm/system/windows.cpp @@ -23,8 +23,74 @@ #undef max #undef min -#include "arch.h" -#include "system.h" +#include "avian/arch.h" +#include +#include + +#if defined(WINAPI_FAMILY) + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + +#define WaitForSingleObject(hHandle, dwMilliseconds) \ + WaitForSingleObjectEx((hHandle), (dwMilliseconds), FALSE) + +#define CreateEvent(lpEventAttributes, bManualReset, bInitialState, lpName) \ + CreateEventEx((lpEventAttributes), (lpName), ((bManualReset)?CREATE_EVENT_MANUAL_RESET:0)|((bInitialState)?CREATE_EVENT_INITIAL_SET:0), EVENT_ALL_ACCESS) + +#define CreateMutex(lpEventAttributes, bInitialOwner, lpName) \ + CreateMutexEx((lpEventAttributes), (lpName), (bInitialOwner)?CREATE_MUTEX_INITIAL_OWNER:0, MUTEX_ALL_ACCESS) + +#include "thread-emulation.h" + +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) +// Headers in Windows Phone 8 DevKit contain severe error, so let's define needed functions on our own +extern "C" +{ +WINBASEAPI +_Ret_maybenull_ +HANDLE +WINAPI +CreateFileMappingFromApp( + _In_ HANDLE hFile, + _In_opt_ PSECURITY_ATTRIBUTES SecurityAttributes, + _In_ ULONG PageProtection, + _In_ ULONG64 MaximumSize, + _In_opt_ PCWSTR Name + ); + +WINBASEAPI +_Ret_maybenull_ __out_data_source(FILE) +PVOID +WINAPI +MapViewOfFileFromApp( + _In_ HANDLE hFileMappingObject, + _In_ ULONG DesiredAccess, + _In_ ULONG64 FileOffset, + _In_ SIZE_T NumberOfBytesToMap + ); + +WINBASEAPI +BOOL +WINAPI +UnmapViewOfFile( + _In_ LPCVOID lpBaseAddress + ); +} +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE) + +#else + +#ifndef WINAPI_PARTITION_DESKTOP +#define WINAPI_PARTITION_DESKTOP 1 +#endif + +#ifndef WINAPI_FAMILY_PARTITION +#define WINAPI_FAMILY_PARTITION(x) (x) +#endif + +#endif #define ACQUIRE(s, x) MutexResource MAKE_NAME(mutexResource_) (s, x) @@ -57,8 +123,10 @@ const unsigned HandlerCount = 2; class MySystem; MySystem* system; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) LONG CALLBACK handleException(LPEXCEPTION_POINTERS e); +#endif DWORD WINAPI run(void* r) @@ -559,7 +627,9 @@ class MySystem: public System { }; MySystem(const char* crashDumpDirectory): +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) oldHandler(0), +#endif crashDumpDirectory(crashDumpDirectory) { expect(this, system == 0); @@ -581,27 +651,35 @@ class MySystem: public System { int registerHandler(System::SignalHandler* handler, int index) { if (handler) { handlers[index] = handler; - + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (oldHandler == 0) { -#ifdef ARCH_x86_32 +# ifdef ARCH_x86_32 oldHandler = SetUnhandledExceptionFilter(handleException); -#elif defined ARCH_x86_64 +# elif defined ARCH_x86_64 AddVectoredExceptionHandler(1, handleException); oldHandler = reinterpret_cast(1); -#endif +# endif } +#else + #pragma message("TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") +#endif return 0; } else if (handlers[index]) { handlers[index] = 0; if (not findHandler()) { -#ifdef ARCH_x86_32 +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# ifdef ARCH_x86_32 SetUnhandledExceptionFilter(oldHandler); oldHandler = 0; -#elif defined ARCH_x86_64 +# elif defined ARCH_x86_64 // do nothing, handlers are never "unregistered" anyway -#endif +# endif +#else + #pragma message("TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") +#endif } return 0; @@ -618,6 +696,7 @@ class MySystem: public System { if (p) ::free(const_cast(p)); } + #if !defined(AVIAN_AOT_ONLY) virtual void* tryAllocateExecutable(unsigned sizeInBytes) { return VirtualAlloc (0, sizeInBytes, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); @@ -627,6 +706,7 @@ class MySystem: public System { int r UNUSED = VirtualFree(const_cast(p), 0, MEM_RELEASE); assert(this, r); } + #endif virtual bool success(Status s) { return s == 0; @@ -677,6 +757,7 @@ class MySystem: public System { virtual Status visit(System::Thread* st UNUSED, System::Thread* sTarget, ThreadVisitor* visitor) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) assert(this, st != sTarget); Thread* target = static_cast(sTarget); @@ -692,15 +773,15 @@ class MySystem: public System { rv = GetThreadContext(target->thread, &context); if (rv) { -#ifdef ARCH_x86_32 +# ifdef ARCH_x86_32 visitor->visit(reinterpret_cast(context.Eip), - reinterpret_cast(context.Ebp), - reinterpret_cast(context.Esp)); -#elif defined ARCH_x86_64 + reinterpret_cast(context.Esp), + reinterpret_cast(context.Ebp)); +# elif defined ARCH_x86_64 visitor->visit(reinterpret_cast(context.Rip), - reinterpret_cast(context.Rbp), - reinterpret_cast(context.Rsp)); -#endif + reinterpret_cast(context.Rsp), + reinterpret_cast(context.Rbp)); +# endif success = true; } @@ -709,6 +790,10 @@ class MySystem: public System { } return (success ? 0 : 1); +#else + #pragma message("TODO: http://msdn.microsoft.com/en-us/library/windowsphone/develop/system.windows.application.unhandledexception(v=vs.105).aspx") + return false; +#endif } virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types, @@ -719,15 +804,37 @@ class MySystem: public System { virtual Status map(System::Region** region, const char* name) { Status status = 1; - - HANDLE file = CreateFile(name, FILE_READ_DATA, FILE_SHARE_READ, 0, - OPEN_EXISTING, 0, 0); + size_t nameLen = strlen(name) * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar(CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE file = CreateFileW(RUNTIME_ARRAY_BODY(wideName), FILE_READ_DATA, FILE_SHARE_READ, 0, + OPEN_EXISTING, 0, 0); +#else + HANDLE file = CreateFile2(RUNTIME_ARRAY_BODY(wideName), GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, 0); +#endif if (file != INVALID_HANDLE_VALUE) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) unsigned size = GetFileSize(file, 0); +#else + FILE_STANDARD_INFO info; + unsigned size = INVALID_FILE_SIZE; + if(GetFileInformationByHandleEx(file, FileStandardInfo, &info, sizeof(info))) + size = info.EndOfFile.QuadPart; +#endif if (size != INVALID_FILE_SIZE) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0); +#else + HANDLE mapping = CreateFileMappingFromApp(file, 0, PAGE_READONLY, size, 0); +#endif if (mapping) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); +#else + void* data = MapViewOfFileFromApp(mapping, FILE_MAP_READ, 0, 0); +#endif if (data) { *region = new (allocate(this, sizeof(Region))) Region(this, static_cast(data), size, file, mapping); @@ -757,7 +864,12 @@ class MySystem: public System { memcpy(RUNTIME_ARRAY_BODY(buffer) + length, "\\*", 3); Directory* d = new (allocate(this, sizeof(Directory))) Directory(this); + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) d->handle = FindFirstFile(RUNTIME_ARRAY_BODY(buffer), &(d->data)); +#else + d->handle = FindFirstFileEx(RUNTIME_ARRAY_BODY(buffer), FindExInfoStandard, &(d->data), FindExSearchNameMatch, 0, 0); +#endif if (d->handle == INVALID_HANDLE_VALUE) { d->dispose(); } else { @@ -769,21 +881,21 @@ class MySystem: public System { } virtual FileType stat(const char* name, unsigned* length) { - struct _stat s; - int r = _stat(name, &s); - if (r == 0) { - if (S_ISREG(s.st_mode)) { - *length = s.st_size; - return TypeFile; - } else if (S_ISDIR(s.st_mode)) { - *length = 0; + size_t nameLen = strlen(name) * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar(CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesExW + (RUNTIME_ARRAY_BODY(wideName), GetFileExInfoStandard, &data)) + { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { return TypeDirectory; } else { - *length = 0; - return TypeUnknown; + *length = (data.nFileSizeHigh * static_cast(MAXDWORD + 1)) + + data.nFileSizeLow; + return TypeFile; } } else { - *length = 0; return TypeDoesNotExist; } } @@ -797,6 +909,7 @@ class MySystem: public System { } virtual const char* toAbsolutePath(Allocator* allocator, const char* name) { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (strncmp(name, "//", 2) == 0 or strncmp(name, "\\\\", 2) == 0 or strncmp(name + 1, ":/", 2) == 0 @@ -808,6 +921,10 @@ class MySystem: public System { GetCurrentDirectory(MAX_PATH, buffer); return append(allocator, buffer, "\\", name); } +#else + #pragma message("TODO:http://lunarfrog.com/blog/2012/05/21/winrt-folders-access/ Windows.ApplicationModel.Package.Current.InstalledLocation") + return copy(allocator, name); +#endif } virtual Status load(System::Library** lib, @@ -816,9 +933,22 @@ class MySystem: public System { HMODULE handle; unsigned nameLength = (name ? strlen(name) : 0); if (name) { - handle = LoadLibrary(name); + size_t nameLen = nameLength * 2; + RUNTIME_ARRAY(wchar_t, wideName, nameLen + 1); + MultiByteToWideChar(CP_UTF8, 0, name, -1, RUNTIME_ARRAY_BODY(wideName), nameLen + 1); + +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + handle = LoadLibraryW(RUNTIME_ARRAY_BODY(wideName)); +#else + handle = LoadPackagedLibrary(RUNTIME_ARRAY_BODY(wideName), 0); +#endif } else { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) handle = GetModuleHandle(0); +#else + // Most of WinRT/WP8 applications can not host native object files inside main executable + assert(this, false); +#endif } if (handle) { @@ -866,7 +996,11 @@ class MySystem: public System { } virtual void yield() { +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) SwitchToThread(); +#else + YieldProcessor(); +#endif } virtual void exit(int code) { @@ -887,10 +1021,14 @@ class MySystem: public System { HANDLE mutex; SignalHandler* handlers[HandlerCount]; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) LPTOP_LEVEL_EXCEPTION_FILTER oldHandler; +#endif const char* crashDumpDirectory; }; +#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + #pragma pack(push,4) struct MINIDUMP_EXCEPTION_INFORMATION { DWORD thread; @@ -1005,6 +1143,8 @@ handleException(LPEXCEPTION_POINTERS e) return EXCEPTION_CONTINUE_SEARCH; } +#endif + } // namespace namespace vm { diff --git a/src/x86.S b/src/x86.S index 2a0096ec85..cd8682f71d 100644 --- a/src/x86.S +++ b/src/x86.S @@ -8,7 +8,7 @@ There is NO WARRANTY for this software. See license.txt for details. */ -#include "types.h" +#include "avian/types.h" #define LOCAL(x) .L##x diff --git a/src/x86.cpp b/src/x86.cpp deleted file mode 100644 index 06a492dcfd..0000000000 --- a/src/x86.cpp +++ /dev/null @@ -1,3754 +0,0 @@ -/* Copyright (c) 2008-2012, 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 "environment.h" -#include "assembler.h" -#include "target.h" -#include "vector.h" - -#define CAST1(x) reinterpret_cast(x) -#define CAST2(x) reinterpret_cast(x) -#define CAST_BRANCH(x) reinterpret_cast(x) - -using namespace vm; - -namespace { - -namespace local { - -enum { - rax = 0, - rcx = 1, - rdx = 2, - rbx = 3, - rsp = 4, - rbp = 5, - rsi = 6, - rdi = 7, - r8 = 8, - r9 = 9, - r10 = 10, - r11 = 11, - r12 = 12, - r13 = 13, - r14 = 14, - r15 = 15, -}; - -enum { - xmm0 = r15 + 1, - xmm1, - xmm2, - xmm3, - xmm4, - xmm5, - xmm6, - xmm7, - xmm8, - xmm9, - xmm10, - xmm11, - xmm12, - xmm13, - xmm14, - xmm15, -}; - -const unsigned GeneralRegisterMask -= TargetBytesPerWord == 4 ? 0x000000ff : 0x0000ffff; - -const unsigned FloatRegisterMask -= TargetBytesPerWord == 4 ? 0x00ff0000 : 0xffff0000; - -const unsigned FrameHeaderSize = (UseFramePointer ? 2 : 1); - -const int LongJumpRegister = r10; - -const unsigned StackAlignmentInBytes = 16; -const unsigned StackAlignmentInWords = StackAlignmentInBytes / TargetBytesPerWord; - -bool -isInt8(target_intptr_t v) -{ - return v == static_cast(v); -} - -bool -isInt32(target_intptr_t v) -{ - return v == static_cast(v); -} - -class Task; -class AlignmentPadding; - -unsigned -padding(AlignmentPadding* p, unsigned index, unsigned offset, - AlignmentPadding* limit); - -class Context; -class MyBlock; - -ResolvedPromise* -resolved(Context* c, int64_t value); - -class MyBlock: public Assembler::Block { - public: - MyBlock(unsigned offset): - next(0), firstPadding(0), lastPadding(0), offset(offset), start(~0), - size(0) - { } - - virtual unsigned resolve(unsigned start, Assembler::Block* next) { - this->start = start; - this->next = static_cast(next); - - return start + size + padding(firstPadding, start, offset, lastPadding); - } - - MyBlock* next; - AlignmentPadding* firstPadding; - AlignmentPadding* lastPadding; - unsigned offset; - unsigned start; - unsigned size; -}; - -typedef void (*OperationType)(Context*); - -typedef void (*UnaryOperationType)(Context*, unsigned, Assembler::Operand*); - -typedef void (*BinaryOperationType) -(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*); - -typedef void (*BranchOperationType) -(Context*, TernaryOperation, unsigned, Assembler::Operand*, - Assembler::Operand*, Assembler::Operand*); - -class ArchitectureContext { - public: - ArchitectureContext(System* s, bool useNativeFeatures): - s(s), useNativeFeatures(useNativeFeatures) - { } - - System* s; - bool useNativeFeatures; - OperationType operations[OperationCount]; - UnaryOperationType unaryOperations[UnaryOperationCount - * OperandTypeCount]; - BinaryOperationType binaryOperations - [(BinaryOperationCount + NonBranchTernaryOperationCount) - * OperandTypeCount - * OperandTypeCount]; - BranchOperationType branchOperations - [(BranchOperationCount) - * OperandTypeCount - * OperandTypeCount]; -}; - -class Context { - public: - Context(System* s, Allocator* a, Zone* zone, ArchitectureContext* ac): - s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0), - firstBlock(new(zone) MyBlock(0)), - lastBlock(firstBlock), ac(ac) - { } - - System* s; - Zone* zone; - Assembler::Client* client; - Vector code; - Task* tasks; - uint8_t* result; - MyBlock* firstBlock; - MyBlock* lastBlock; - ArchitectureContext* ac; -}; - -void NO_RETURN -abort(Context* c) -{ - abort(c->s); -} - -void NO_RETURN -abort(ArchitectureContext* c) -{ - abort(c->s); -} - -#ifndef NDEBUG -void -assert(Context* c, bool v) -{ - assert(c->s, v); -} - -void -assert(ArchitectureContext* c, bool v) -{ - assert(c->s, v); -} -#endif // not NDEBUG - -ResolvedPromise* -resolved(Context* c, int64_t value) -{ - return new(c->zone) ResolvedPromise(value); -} - -class Offset: public Promise { - public: - Offset(Context* c, MyBlock* block, unsigned offset, AlignmentPadding* limit): - c(c), block(block), offset(offset), limit(limit), value_(-1) - { } - - virtual bool resolved() { - return block->start != static_cast(~0); - } - - virtual int64_t value() { - assert(c, resolved()); - - if (value_ == -1) { - value_ = block->start + (offset - block->offset) - + padding(block->firstPadding, block->start, block->offset, limit); - } - - return value_; - } - - Context* c; - MyBlock* block; - unsigned offset; - AlignmentPadding* limit; - int value_; -}; - -Promise* -offset(Context* c) -{ - return new(c->zone) Offset(c, c->lastBlock, c->code.length(), c->lastBlock->lastPadding); -} - -class Task { - public: - Task(Task* next): next(next) { } - - virtual void run(Context* c) = 0; - - Task* next; -}; - -void* -resolveOffset(System* s, uint8_t* instruction, unsigned instructionSize, - int64_t value) -{ - intptr_t v = reinterpret_cast(value) - - instruction - instructionSize; - - expect(s, isInt32(v)); - - int32_t v4 = v; - memcpy(instruction + instructionSize - 4, &v4, 4); - return instruction + instructionSize; -} - -class OffsetListener: public Promise::Listener { - public: - OffsetListener(System* s, uint8_t* instruction, - unsigned instructionSize): - s(s), - instruction(instruction), - instructionSize(instructionSize) - { } - - virtual bool resolve(int64_t value, void** location) { - void* p = resolveOffset(s, instruction, instructionSize, value); - if (location) *location = p; - return false; - } - - System* s; - uint8_t* instruction; - unsigned instructionSize; -}; - -class OffsetTask: public Task { - public: - OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, - unsigned instructionSize): - Task(next), - promise(promise), - instructionOffset(instructionOffset), - instructionSize(instructionSize) - { } - - virtual void run(Context* c) { - if (promise->resolved()) { - resolveOffset - (c->s, c->result + instructionOffset->value(), instructionSize, - promise->value()); - } else { - new (promise->listen(sizeof(OffsetListener))) - OffsetListener(c->s, c->result + instructionOffset->value(), - instructionSize); - } - } - - Promise* promise; - Promise* instructionOffset; - unsigned instructionSize; -}; - -void -appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, - unsigned instructionSize) -{ - OffsetTask* task = - new(c->zone) OffsetTask(c->tasks, promise, instructionOffset, instructionSize); - - c->tasks = task; -} - -void -copy(System* s, void* dst, int64_t src, unsigned size) -{ - switch (size) { - case 4: { - int32_t v = src; - memcpy(dst, &v, 4); - } break; - - case 8: { - int64_t v = src; - memcpy(dst, &v, 8); - } break; - - default: abort(s); - } -} - -class ImmediateListener: public Promise::Listener { - public: - ImmediateListener(System* s, void* dst, unsigned size, unsigned offset): - s(s), dst(dst), size(size), offset(offset) - { } - - virtual bool resolve(int64_t value, void** location) { - copy(s, dst, value, size); - if (location) *location = static_cast(dst) + offset; - return offset == 0; - } - - System* s; - void* dst; - unsigned size; - unsigned offset; -}; - -class ImmediateTask: public Task { - public: - ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, - unsigned promiseOffset): - Task(next), - promise(promise), - offset(offset), - size(size), - promiseOffset(promiseOffset) - { } - - virtual void run(Context* c) { - if (promise->resolved()) { - copy(c->s, c->result + offset->value(), promise->value(), size); - } else { - new (promise->listen(sizeof(ImmediateListener))) ImmediateListener - (c->s, c->result + offset->value(), size, promiseOffset); - } - } - - Promise* promise; - Promise* offset; - unsigned size; - unsigned promiseOffset; -}; - -void -appendImmediateTask(Context* c, Promise* promise, Promise* offset, - unsigned size, unsigned promiseOffset = 0) -{ - c->tasks = new(c->zone) ImmediateTask - (c->tasks, promise, offset, size, promiseOffset); -} - -class AlignmentPadding { - public: - AlignmentPadding(Context* c, unsigned instructionOffset, unsigned alignment): - offset(c->code.length()), - instructionOffset(instructionOffset), - alignment(alignment), - next(0), - padding(-1) - { - if (c->lastBlock->firstPadding) { - c->lastBlock->lastPadding->next = this; - } else { - c->lastBlock->firstPadding = this; - } - c->lastBlock->lastPadding = this; - } - - unsigned offset; - unsigned instructionOffset; - unsigned alignment; - AlignmentPadding* next; - int padding; -}; - -unsigned -padding(AlignmentPadding* p, unsigned start, unsigned offset, - AlignmentPadding* limit) -{ - unsigned padding = 0; - if (limit) { - if (limit->padding == -1) { - for (; p; p = p->next) { - if (p->padding == -1) { - unsigned index = p->offset - offset; - while ((start + index + padding + p->instructionOffset) - % p->alignment) - { - ++ padding; - } - - p->padding = padding; - - if (p == limit) break; - } else { - padding = p->padding; - } - } - } else { - padding = limit->padding; - } - } - return padding; -} - -extern "C" bool -detectFeature(unsigned ecx, unsigned edx); - -bool -useSSE(ArchitectureContext* c) -{ - if (TargetBytesPerWord == 8) { - // amd64 implies SSE2 support - return true; - } else if (c->useNativeFeatures) { - static int supported = -1; - if (supported == -1) { - supported = detectFeature(0, 0x2000000) // SSE 1 - and detectFeature(0, 0x4000000); // SSE 2 - } - return supported; - } else { - return false; - } -} - -#define REX_W 0x48 -#define REX_R 0x44 -#define REX_X 0x42 -#define REX_B 0x41 -#define REX_NONE 0x40 - -void maybeRex(Context* c, unsigned size, int a, int index, int base, - bool always) -{ - if (TargetBytesPerWord == 8) { - uint8_t byte; - if (size == 8) { - byte = REX_W; - } else { - byte = REX_NONE; - } - if (a != NoRegister and (a & 8)) byte |= REX_R; - if (index != NoRegister and (index & 8)) byte |= REX_X; - if (base != NoRegister and (base & 8)) byte |= REX_B; - if (always or byte != REX_NONE) c->code.append(byte); - } -} - -void -maybeRex(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b) -{ - maybeRex(c, size, a->low, NoRegister, b->low, false); -} - -void -alwaysRex(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b) -{ - maybeRex(c, size, a->low, NoRegister, b->low, true); -} - -void -maybeRex(Context* c, unsigned size, Assembler::Register* a) -{ - maybeRex(c, size, NoRegister, NoRegister, a->low, false); -} - -void -maybeRex(Context* c, unsigned size, Assembler::Register* a, - Assembler::Memory* b) -{ - maybeRex(c, size, a->low, b->index, b->base, size == 1 and (a->low & 4)); -} - -void -maybeRex(Context* c, unsigned size, Assembler::Memory* a) -{ - maybeRex(c, size, NoRegister, a->index, a->base, false); -} - -int -regCode(int a) -{ - return a & 7; -} - -int -regCode(Assembler::Register* a) -{ - return regCode(a->low); -} - -void -modrm(Context* c, uint8_t mod, int a, int b) -{ - c->code.append(mod | (regCode(b) << 3) | regCode(a)); -} - -void -modrm(Context* c, uint8_t mod, Assembler::Register* a, Assembler::Register* b) -{ - modrm(c, mod, a->low, b->low); -} - -void -sib(Context* c, unsigned scale, int index, int base) -{ - c->code.append((log(scale) << 6) | (regCode(index) << 3) | regCode(base)); -} - -void -modrmSib(Context* c, int width, int a, int scale, int index, int base) -{ - if (index == NoRegister) { - modrm(c, width, base, a); - if (regCode(base) == rsp) { - sib(c, 0x00, rsp, rsp); - } - } else { - modrm(c, width, rsp, a); - sib(c, scale, index, base); - } -} - -void -modrmSibImm(Context* c, int a, int scale, int index, int base, int offset) -{ - if (offset == 0 and regCode(base) != rbp) { - modrmSib(c, 0x00, a, scale, index, base); - } else if (isInt8(offset)) { - modrmSib(c, 0x40, a, scale, index, base); - c->code.append(offset); - } else { - modrmSib(c, 0x80, a, scale, index, base); - c->code.append4(offset); - } -} - - -void -modrmSibImm(Context* c, Assembler::Register* a, Assembler::Memory* b) -{ - modrmSibImm(c, a->low, b->scale, b->index, b->base, b->offset); -} - -void -opcode(Context* c, uint8_t op) -{ - c->code.append(op); -} - -void -opcode(Context* c, uint8_t op1, uint8_t op2) -{ - c->code.append(op1); - c->code.append(op2); -} - -void -return_(Context* c) -{ - opcode(c, 0xc3); -} - -void -trap(Context* c) -{ - opcode(c, 0xcc); -} - -void -ignore(Context*) -{ } - -void -storeLoadBarrier(Context* c) -{ - if (useSSE(c->ac)) { - // mfence: - c->code.append(0x0f); - c->code.append(0xae); - c->code.append(0xf0); - } else { - // lock addq $0x0,(%rsp): - c->code.append(0xf0); - if (TargetBytesPerWord == 8) { - c->code.append(0x48); - } - c->code.append(0x83); - c->code.append(0x04); - c->code.append(0x24); - c->code.append(0x00); - } -} - -void -unconditional(Context* c, unsigned jump, Assembler::Constant* a) -{ - appendOffsetTask(c, a->value, offset(c), 5); - - opcode(c, jump); - c->code.append4(0); -} - -void -conditional(Context* c, unsigned condition, Assembler::Constant* a) -{ - appendOffsetTask(c, a->value, offset(c), 6); - - opcode(c, 0x0f, condition); - c->code.append4(0); -} - -unsigned -index(ArchitectureContext*, UnaryOperation operation, OperandType operand) -{ - return operation + (UnaryOperationCount * operand); -} - -unsigned -index(ArchitectureContext*, BinaryOperation operation, - OperandType operand1, - OperandType operand2) -{ - return operation - + ((BinaryOperationCount + NonBranchTernaryOperationCount) * operand1) - + ((BinaryOperationCount + NonBranchTernaryOperationCount) - * OperandTypeCount * operand2); -} - -bool -isBranch(TernaryOperation op) -{ - return op > FloatMin; -} - -bool -isFloatBranch(TernaryOperation op) -{ - return op > JumpIfNotEqual; -} - -unsigned -index(ArchitectureContext* c UNUSED, TernaryOperation operation, - OperandType operand1, OperandType operand2) -{ - assert(c, not isBranch(operation)); - - return BinaryOperationCount + operation - + ((BinaryOperationCount + NonBranchTernaryOperationCount) * operand1) - + ((BinaryOperationCount + NonBranchTernaryOperationCount) - * OperandTypeCount * operand2); -} - -unsigned -branchIndex(ArchitectureContext* c UNUSED, OperandType operand1, - OperandType operand2) -{ - return operand1 + (OperandTypeCount * operand2); -} - -void -moveCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b); - -void -moveCR2(Context*, unsigned, Assembler::Constant*, unsigned, - Assembler::Register*, unsigned); - -void -callR(Context*, unsigned, Assembler::Register*); - -void -callC(Context* c, unsigned size UNUSED, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - unconditional(c, 0xe8, a); -} - -void -longCallC(Context* c, unsigned size, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - if (TargetBytesPerWord == 8) { - Assembler::Register r(LongJumpRegister); - moveCR2(c, size, a, size, &r, 11); - callR(c, size, &r); - } else { - callC(c, size, a); - } -} - -void -jumpR(Context* c, unsigned size UNUSED, Assembler::Register* a) -{ - assert(c, size == TargetBytesPerWord); - - maybeRex(c, 4, a); - opcode(c, 0xff, 0xe0 + regCode(a)); -} - -void -jumpC(Context* c, unsigned size UNUSED, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - unconditional(c, 0xe9, a); -} - -void -jumpM(Context* c, unsigned size UNUSED, Assembler::Memory* a) -{ - assert(c, size == TargetBytesPerWord); - - maybeRex(c, 4, a); - opcode(c, 0xff); - modrmSibImm(c, rsp, a->scale, a->index, a->base, a->offset); -} - -void -longJumpC(Context* c, unsigned size, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - if (TargetBytesPerWord == 8) { - Assembler::Register r(LongJumpRegister); - moveCR2(c, size, a, size, &r, 11); - jumpR(c, size, &r); - } else { - jumpC(c, size, a); - } -} - -void -callR(Context* c, unsigned size UNUSED, Assembler::Register* a) -{ - assert(c, size == TargetBytesPerWord); - - // maybeRex.W has no meaning here so we disable it - maybeRex(c, 4, a); - opcode(c, 0xff, 0xd0 + regCode(a)); -} - -void -callM(Context* c, unsigned size UNUSED, Assembler::Memory* a) -{ - assert(c, size == TargetBytesPerWord); - - maybeRex(c, 4, a); - opcode(c, 0xff); - modrmSibImm(c, rdx, a->scale, a->index, a->base, a->offset); -} - -void -alignedCallC(Context* c, unsigned size, Assembler::Constant* a) -{ - new(c->zone) AlignmentPadding(c, 1, 4); - callC(c, size, a); -} - -void -alignedLongCallC(Context* c, unsigned size, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - if (TargetBytesPerWord == 8) { - new (c->zone) AlignmentPadding(c, 2, 8); - longCallC(c, size, a); - } else { - alignedCallC(c, size, a); - } -} - -void -alignedJumpC(Context* c, unsigned size, Assembler::Constant* a) -{ - new (c->zone) AlignmentPadding(c, 1, 4); - jumpC(c, size, a); -} - -void -alignedLongJumpC(Context* c, unsigned size, Assembler::Constant* a) -{ - assert(c, size == TargetBytesPerWord); - - if (TargetBytesPerWord == 8) { - new (c->zone) AlignmentPadding(c, 2, 8); - longJumpC(c, size, a); - } else { - alignedJumpC(c, size, a); - } -} - -void -pushR(Context* c, unsigned size, Assembler::Register* a) -{ - if (TargetBytesPerWord == 4 and size == 8) { - Assembler::Register ah(a->high); - - pushR(c, 4, &ah); - pushR(c, 4, a); - } else { - maybeRex(c, 4, a); - opcode(c, 0x50 + regCode(a)); - } -} - -void -moveRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b); - -void -popR(Context* c, unsigned size, Assembler::Register* a) -{ - if (TargetBytesPerWord == 4 and size == 8) { - Assembler::Register ah(a->high); - - popR(c, 4, a); - popR(c, 4, &ah); - } else { - maybeRex(c, 4, a); - opcode(c, 0x58 + regCode(a)); - if (TargetBytesPerWord == 8 and size == 4) { - moveRR(c, 4, a, 8, a); - } - } -} - -void -addCarryCR(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b); - -void -negateR(Context* c, unsigned size, Assembler::Register* a) -{ - if (TargetBytesPerWord == 4 and size == 8) { - assert(c, a->low == rax and a->high == rdx); - - ResolvedPromise zeroPromise(0); - Assembler::Constant zero(&zeroPromise); - - Assembler::Register ah(a->high); - - negateR(c, 4, a); - addCarryCR(c, 4, &zero, &ah); - negateR(c, 4, &ah); - } else { - maybeRex(c, size, a); - opcode(c, 0xf7, 0xd8 + regCode(a)); - } -} - -void -negateRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b UNUSED) -{ - assert(c, aSize == bSize); - - negateR(c, aSize, a); -} - -void -moveCR2(Context* c, UNUSED unsigned aSize, Assembler::Constant* a, - UNUSED unsigned bSize, Assembler::Register* b, unsigned promiseOffset) -{ - if (TargetBytesPerWord == 4 and bSize == 8) { - int64_t v = a->value->value(); - - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - moveCR(c, 4, &al, 4, b); - moveCR(c, 4, &ah, 4, &bh); - } else { - maybeRex(c, TargetBytesPerWord, b); - opcode(c, 0xb8 + regCode(b)); - if (a->value->resolved()) { - c->code.appendTargetAddress(a->value->value()); - } else { - appendImmediateTask - (c, a->value, offset(c), TargetBytesPerWord, promiseOffset); - c->code.appendTargetAddress(static_cast(0)); - } - } -} - -bool -floatReg(Assembler::Register* a) -{ - return a->low >= xmm0; -} - -void -sseMoveRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize >= 4); - assert(c, aSize == bSize); - - if (floatReg(a) and floatReg(b)) { - if (aSize == 4) { - opcode(c, 0xf3); - maybeRex(c, 4, a, b); - opcode(c, 0x0f, 0x10); - modrm(c, 0xc0, a, b); - } else { - opcode(c, 0xf2); - maybeRex(c, 4, b, a); - opcode(c, 0x0f, 0x10); - modrm(c, 0xc0, a, b); - } - } else if (floatReg(a)) { - opcode(c, 0x66); - maybeRex(c, aSize, a, b); - opcode(c, 0x0f, 0x7e); - modrm(c, 0xc0, b, a); - } else { - opcode(c, 0x66); - maybeRex(c, aSize, b, a); - opcode(c, 0x0f, 0x6e); - modrm(c, 0xc0, a, b); - } -} - -void -sseMoveCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize <= TargetBytesPerWord); - Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); - moveCR2(c, aSize, a, aSize, &tmp, 0); - sseMoveRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); -} - -void -moveCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - if (floatReg(b)) { - sseMoveCR(c, aSize, a, bSize, b); - } else { - moveCR2(c, aSize, a, bSize, b, 0); - } -} - -void -swapRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - assert(c, aSize == TargetBytesPerWord); - - alwaysRex(c, aSize, a, b); - opcode(c, 0x87); - modrm(c, 0xc0, b, a); -} - -void -moveRR(Context* c, unsigned aSize, Assembler::Register* a, - UNUSED unsigned bSize, Assembler::Register* b) -{ - if (floatReg(a) or floatReg(b)) { - sseMoveRR(c, aSize, a, bSize, b); - return; - } - - if (TargetBytesPerWord == 4 and aSize == 8 and bSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - if (a->high == b->low) { - if (a->low == b->high) { - swapRR(c, 4, a, 4, b); - } else { - moveRR(c, 4, &ah, 4, &bh); - moveRR(c, 4, a, 4, b); - } - } else { - moveRR(c, 4, a, 4, b); - moveRR(c, 4, &ah, 4, &bh); - } - } else { - switch (aSize) { - case 1: - if (TargetBytesPerWord == 4 and a->low > rbx) { - assert(c, b->low <= rbx); - - moveRR(c, TargetBytesPerWord, a, TargetBytesPerWord, b); - moveRR(c, 1, b, TargetBytesPerWord, b); - } else { - alwaysRex(c, aSize, b, a); - opcode(c, 0x0f, 0xbe); - modrm(c, 0xc0, a, b); - } - break; - - case 2: - alwaysRex(c, aSize, b, a); - opcode(c, 0x0f, 0xbf); - modrm(c, 0xc0, a, b); - break; - - case 4: - if (bSize == 8) { - if (TargetBytesPerWord == 8) { - alwaysRex(c, bSize, b, a); - opcode(c, 0x63); - modrm(c, 0xc0, a, b); - } else { - if (a->low == rax and b->low == rax and b->high == rdx) { - opcode(c, 0x99); //cdq - } else { - assert(c, b->low == rax and b->high == rdx); - - moveRR(c, 4, a, 4, b); - moveRR(c, 4, b, 8, b); - } - } - } else { - if (a->low != b->low) { - alwaysRex(c, aSize, a, b); - opcode(c, 0x89); - modrm(c, 0xc0, b, a); - } - } - break; - - case 8: - if (a->low != b->low){ - maybeRex(c, aSize, a, b); - opcode(c, 0x89); - modrm(c, 0xc0, b, a); - } - break; - } - } -} - -void -sseMoveMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize >= 4); - - if (TargetBytesPerWord == 4 and aSize == 8) { - opcode(c, 0xf3); - opcode(c, 0x0f, 0x7e); - modrmSibImm(c, b, a); - } else { - opcode(c, 0x66); - maybeRex(c, aSize, b, a); - opcode(c, 0x0f, 0x6e); - modrmSibImm(c, b, a); - } -} - -void -moveMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize, Assembler::Register* b) -{ - if (floatReg(b)) { - sseMoveMR(c, aSize, a, bSize, b); - return; - } - - switch (aSize) { - case 1: - maybeRex(c, bSize, b, a); - opcode(c, 0x0f, 0xbe); - modrmSibImm(c, b, a); - break; - - case 2: - maybeRex(c, bSize, b, a); - opcode(c, 0x0f, 0xbf); - modrmSibImm(c, b, a); - break; - - case 4: - if (TargetBytesPerWord == 8) { - maybeRex(c, bSize, b, a); - opcode(c, 0x63); - modrmSibImm(c, b, a); - } else { - if (bSize == 8) { - assert(c, b->low == rax and b->high == rdx); - - moveMR(c, 4, a, 4, b); - moveRR(c, 4, b, 8, b); - } else { - maybeRex(c, bSize, b, a); - opcode(c, 0x8b); - modrmSibImm(c, b, a); - } - } - break; - - case 8: - if (TargetBytesPerWord == 4 and bSize == 8) { - Assembler::Memory ah(a->base, a->offset + 4, a->index, a->scale); - Assembler::Register bh(b->high); - - moveMR(c, 4, a, 4, b); - moveMR(c, 4, &ah, 4, &bh); - } else { - maybeRex(c, bSize, b, a); - opcode(c, 0x8b); - modrmSibImm(c, b, a); - } - break; - - default: abort(c); - } -} - -void -sseMoveRM(Context* c, unsigned aSize, Assembler::Register* a, - UNUSED unsigned bSize, Assembler::Memory* b) -{ - assert(c, aSize >= 4); - assert(c, aSize == bSize); - - if (TargetBytesPerWord == 4 and aSize == 8) { - opcode(c, 0x66); - opcode(c, 0x0f, 0xd6); - modrmSibImm(c, a, b); - } else { - opcode(c, 0x66); - maybeRex(c, aSize, a, b); - opcode(c, 0x0f, 0x7e); - modrmSibImm(c, a, b); - } -} - -void -moveRM(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Memory* b) -{ - assert(c, aSize == bSize); - - if (floatReg(a)) { - sseMoveRM(c, aSize, a, bSize, b); - return; - } - - switch (aSize) { - case 1: - maybeRex(c, bSize, a, b); - opcode(c, 0x88); - modrmSibImm(c, a, b); - break; - - case 2: - opcode(c, 0x66); - maybeRex(c, bSize, a, b); - opcode(c, 0x89); - modrmSibImm(c, a, b); - break; - - case 4: - if (TargetBytesPerWord == 8) { - maybeRex(c, bSize, a, b); - opcode(c, 0x89); - modrmSibImm(c, a, b); - break; - } else { - opcode(c, 0x89); - modrmSibImm(c, a, b); - } - break; - - case 8: - if (TargetBytesPerWord == 8) { - maybeRex(c, bSize, a, b); - opcode(c, 0x89); - modrmSibImm(c, a, b); - } else { - Assembler::Register ah(a->high); - Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale); - - moveRM(c, 4, a, 4, b); - moveRM(c, 4, &ah, 4, &bh); - } - break; - - default: abort(c); - } -} - -void -moveAR(Context* c, unsigned aSize, Assembler::Address* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, TargetBytesPerWord == 8 or (aSize == 4 and bSize == 4)); - - Assembler::Constant constant(a->address); - Assembler::Memory memory(b->low, 0, -1, 0); - - moveCR(c, aSize, &constant, bSize, b); - moveMR(c, bSize, &memory, bSize, b); -} - -ShiftMaskPromise* -shiftMaskPromise(Context* c, Promise* base, unsigned shift, int64_t mask) -{ - return new(c->zone) ShiftMaskPromise(base, shift, mask); -} - -void -moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, - unsigned bSize, Assembler::Memory* b) -{ - switch (bSize) { - case 1: - maybeRex(c, bSize, b); - opcode(c, 0xc6); - modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); - c->code.append(a->value->value()); - break; - - case 2: - opcode(c, 0x66); - maybeRex(c, bSize, b); - opcode(c, 0xc7); - modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); - c->code.append2(a->value->value()); - break; - - case 4: - maybeRex(c, bSize, b); - opcode(c, 0xc7); - modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); - if (a->value->resolved()) { - c->code.append4(a->value->value()); - } else { - appendImmediateTask(c, a->value, offset(c), 4); - c->code.append4(0); - } - break; - - case 8: { - if (TargetBytesPerWord == 8) { - if (a->value->resolved() and isInt32(a->value->value())) { - maybeRex(c, bSize, b); - opcode(c, 0xc7); - modrmSibImm(c, 0, b->scale, b->index, b->base, b->offset); - c->code.append4(a->value->value()); - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, 8, a, 8, &tmp); - moveRM(c, 8, &tmp, 8, b); - c->client->releaseTemporary(tmp.low); - } - } else { - Assembler::Constant ah(shiftMaskPromise(c, a->value, 32, 0xFFFFFFFF)); - Assembler::Constant al(shiftMaskPromise(c, a->value, 0, 0xFFFFFFFF)); - - Assembler::Memory bh(b->base, b->offset + 4, b->index, b->scale); - - moveCM(c, 4, &al, 4, b); - moveCM(c, 4, &ah, 4, &bh); - } - } break; - - default: abort(c); - } -} - -void -moveZRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - switch (aSize) { - case 2: - alwaysRex(c, aSize, b, a); - opcode(c, 0x0f, 0xb7); - modrm(c, 0xc0, a, b); - break; - - default: abort(c); - } -} - -void -moveZMR(Context* c, unsigned aSize UNUSED, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, bSize == TargetBytesPerWord); - assert(c, aSize == 2); - - maybeRex(c, bSize, b, a); - opcode(c, 0x0f, 0xb7); - modrmSibImm(c, b->low, a->scale, a->index, a->base, a->offset); -} - -void -addCarryRR(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b) -{ - assert(c, TargetBytesPerWord == 8 or size == 4); - - maybeRex(c, size, a, b); - opcode(c, 0x11); - modrm(c, 0xc0, b, a); -} - -void -addRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - if (TargetBytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - addRR(c, 4, a, 4, b); - addCarryRR(c, 4, &ah, &bh); - } else { - maybeRex(c, aSize, a, b); - opcode(c, 0x01); - modrm(c, 0xc0, b, a); - } -} - -void -addCarryCR(Context* c, unsigned size, Assembler::Constant* a, - Assembler::Register* b) -{ - - int64_t v = a->value->value(); - maybeRex(c, size, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xd0 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xd0 + regCode(b)); - c->code.append4(v); - } -} - -void -addCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - int64_t v = a->value->value(); - if (v) { - if (TargetBytesPerWord == 4 and bSize == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - addCR(c, 4, &al, 4, b); - addCarryCR(c, 4, &ah, &bh); - } else { - if (isInt32(v)) { - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xc0 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xc0 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - addRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } - } -} - -void -subtractBorrowCR(Context* c, unsigned size UNUSED, Assembler::Constant* a, - Assembler::Register* b) -{ - assert(c, TargetBytesPerWord == 8 or size == 4); - - int64_t v = a->value->value(); - if (isInt8(v)) { - opcode(c, 0x83, 0xd8 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xd8 + regCode(b)); - c->code.append4(v); - } -} - -void -subtractRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b); - -void -subtractCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - int64_t v = a->value->value(); - if (v) { - if (TargetBytesPerWord == 4 and bSize == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - subtractCR(c, 4, &al, 4, b); - subtractBorrowCR(c, 4, &ah, &bh); - } else { - if (isInt32(v)) { - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xe8 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xe8 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - subtractRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } - } -} - -void -subtractBorrowRR(Context* c, unsigned size, Assembler::Register* a, - Assembler::Register* b) -{ - assert(c, TargetBytesPerWord == 8 or size == 4); - - maybeRex(c, size, a, b); - opcode(c, 0x19); - modrm(c, 0xc0, b, a); -} - -void -subtractRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - if (TargetBytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - subtractRR(c, 4, a, 4, b); - subtractBorrowRR(c, 4, &ah, &bh); - } else { - maybeRex(c, aSize, a, b); - opcode(c, 0x29); - modrm(c, 0xc0, b, a); - } -} - -void -andRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - - if (TargetBytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - andRR(c, 4, a, 4, b); - andRR(c, 4, &ah, 4, &bh); - } else { - maybeRex(c, aSize, a, b); - opcode(c, 0x21); - modrm(c, 0xc0, b, a); - } -} - -void -andCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - int64_t v = a->value->value(); - - if (TargetBytesPerWord == 4 and bSize == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - andCR(c, 4, &al, 4, b); - andCR(c, 4, &ah, 4, &bh); - } else { - if (isInt32(v)) { - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xe0 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xe0 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - andRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } -} - -void -orRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - if (TargetBytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - orRR(c, 4, a, 4, b); - orRR(c, 4, &ah, 4, &bh); - } else { - maybeRex(c, aSize, a, b); - opcode(c, 0x09); - modrm(c, 0xc0, b, a); - } -} - -void -orCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - int64_t v = a->value->value(); - if (v) { - if (TargetBytesPerWord == 4 and bSize == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - orCR(c, 4, &al, 4, b); - orCR(c, 4, &ah, 4, &bh); - } else { - if (isInt32(v)) { - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xc8 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xc8 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - orRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } - } -} - -void -xorRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - if (TargetBytesPerWord == 4 and aSize == 8) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - xorRR(c, 4, a, 4, b); - xorRR(c, 4, &ah, 4, &bh); - } else { - maybeRex(c, aSize, a, b); - opcode(c, 0x31); - modrm(c, 0xc0, b, a); - } -} - -void -xorCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - int64_t v = a->value->value(); - if (v) { - if (TargetBytesPerWord == 4 and bSize == 8) { - ResolvedPromise high((v >> 32) & 0xFFFFFFFF); - Assembler::Constant ah(&high); - - ResolvedPromise low(v & 0xFFFFFFFF); - Assembler::Constant al(&low); - - Assembler::Register bh(b->high); - - xorCR(c, 4, &al, 4, b); - xorCR(c, 4, &ah, 4, &bh); - } else { - if (isInt32(v)) { - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xf0 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xf0 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - xorRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } - } -} - -void -multiplyRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - - if (TargetBytesPerWord == 4 and aSize == 8) { - assert(c, b->high == rdx); - assert(c, b->low != rax); - assert(c, a->low != rax); - assert(c, a->high != rax); - - c->client->save(rax); - - Assembler::Register axdx(rax, rdx); - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - Assembler::Register tmp(-1); - Assembler::Register* scratch; - if (a->low == b->low) { - tmp.low = c->client->acquireTemporary - (GeneralRegisterMask & ~(1 << rax)); - scratch = &tmp; - moveRR(c, 4, b, 4, scratch); - } else { - scratch = b; - } - - moveRR(c, 4, b, 4, &axdx); - multiplyRR(c, 4, &ah, 4, scratch); - multiplyRR(c, 4, a, 4, &bh); - addRR(c, 4, &bh, 4, scratch); - - // mul a->low,%eax%edx - opcode(c, 0xf7, 0xe0 + a->low); - - addRR(c, 4, scratch, 4, &bh); - moveRR(c, 4, &axdx, 4, b); - - if (tmp.low != -1) { - c->client->releaseTemporary(tmp.low); - } - } else { - maybeRex(c, aSize, b, a); - opcode(c, 0x0f, 0xaf); - modrm(c, 0xc0, a, b); - } -} - -void -branch(Context* c, TernaryOperation op, Assembler::Constant* target) -{ - switch (op) { - case JumpIfEqual: - conditional(c, 0x84, target); - break; - - case JumpIfNotEqual: - conditional(c, 0x85, target); - break; - - case JumpIfLess: - conditional(c, 0x8c, target); - break; - - case JumpIfGreater: - conditional(c, 0x8f, target); - break; - - case JumpIfLessOrEqual: - conditional(c, 0x8e, target); - break; - - case JumpIfGreaterOrEqual: - conditional(c, 0x8d, target); - break; - - default: - abort(c); - } -} - -void -branchFloat(Context* c, TernaryOperation op, Assembler::Constant* target) -{ - switch (op) { - case JumpIfFloatEqual: - conditional(c, 0x84, target); - break; - - case JumpIfFloatNotEqual: - conditional(c, 0x85, target); - break; - - case JumpIfFloatLess: - conditional(c, 0x82, target); - break; - - case JumpIfFloatGreater: - conditional(c, 0x87, target); - break; - - case JumpIfFloatLessOrEqual: - conditional(c, 0x86, target); - break; - - case JumpIfFloatGreaterOrEqual: - conditional(c, 0x83, target); - break; - - case JumpIfFloatLessOrUnordered: - conditional(c, 0x82, target); - conditional(c, 0x8a, target); - break; - - case JumpIfFloatGreaterOrUnordered: - conditional(c, 0x87, target); - conditional(c, 0x8a, target); - break; - - case JumpIfFloatLessOrEqualOrUnordered: - conditional(c, 0x86, target); - conditional(c, 0x8a, target); - break; - - case JumpIfFloatGreaterOrEqualOrUnordered: - conditional(c, 0x83, target); - conditional(c, 0x8a, target); - break; - - default: - abort(c); - } -} - -void -compareRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - assert(c, aSize <= TargetBytesPerWord); - - maybeRex(c, aSize, a, b); - opcode(c, 0x39); - modrm(c, 0xc0, b, a); -} - -void -compareCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - assert(c, TargetBytesPerWord == 8 or aSize == 4); - - if (a->value->resolved() and isInt32(a->value->value())) { - int64_t v = a->value->value(); - maybeRex(c, aSize, b); - if (isInt8(v)) { - opcode(c, 0x83, 0xf8 + regCode(b)); - c->code.append(v); - } else { - opcode(c, 0x81, 0xf8 + regCode(b)); - c->code.append4(v); - } - } else { - Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - compareRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } -} - -void -compareRM(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Memory* b) -{ - assert(c, aSize == bSize); - assert(c, TargetBytesPerWord == 8 or aSize == 4); - - if (TargetBytesPerWord == 8 and aSize == 4) { - moveRR(c, 4, a, 8, a); - } - maybeRex(c, bSize, a, b); - opcode(c, 0x39); - modrmSibImm(c, a, b); -} - -void -compareCM(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Memory* b) -{ - assert(c, aSize == bSize); - assert(c, TargetBytesPerWord == 8 or aSize == 4); - - if (a->value->resolved()) { - int64_t v = a->value->value(); - maybeRex(c, aSize, b); - opcode(c, isInt8(v) ? 0x83 : 0x81); - modrmSibImm(c, rdi, b->scale, b->index, b->base, b->offset); - - if (isInt8(v)) { - c->code.append(v); - } else if (isInt32(v)) { - c->code.append4(v); - } else { - abort(c); - } - } else { - Assembler::Register tmp(c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, bSize, &tmp); - compareRM(c, bSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } -} - -void -compareFloatRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - if (aSize == 8) { - opcode(c, 0x66); - } - maybeRex(c, 4, a, b); - opcode(c, 0x0f, 0x2e); - modrm(c, 0xc0, a, b); -} - -void -branchLong(Context* c, TernaryOperation op, Assembler::Operand* al, - Assembler::Operand* ah, Assembler::Operand* bl, - Assembler::Operand* bh, Assembler::Constant* target, - BinaryOperationType compare) -{ - compare(c, 4, ah, 4, bh); - - unsigned next = 0; - - switch (op) { - case JumpIfEqual: - opcode(c, 0x75); // jne - next = c->code.length(); - c->code.append(0); - - compare(c, 4, al, 4, bl); - conditional(c, 0x84, target); // je - break; - - case JumpIfNotEqual: - conditional(c, 0x85, target); // jne - - compare(c, 4, al, 4, bl); - conditional(c, 0x85, target); // jne - break; - - case JumpIfLess: - conditional(c, 0x8c, target); // jl - - opcode(c, 0x7f); // jg - next = c->code.length(); - c->code.append(0); - - compare(c, 4, al, 4, bl); - conditional(c, 0x82, target); // jb - break; - - case JumpIfGreater: - conditional(c, 0x8f, target); // jg - - opcode(c, 0x7c); // jl - next = c->code.length(); - c->code.append(0); - - compare(c, 4, al, 4, bl); - conditional(c, 0x87, target); // ja - break; - - case JumpIfLessOrEqual: - conditional(c, 0x8c, target); // jl - - opcode(c, 0x7f); // jg - next = c->code.length(); - c->code.append(0); - - compare(c, 4, al, 4, bl); - conditional(c, 0x86, target); // jbe - break; - - case JumpIfGreaterOrEqual: - conditional(c, 0x8f, target); // jg - - opcode(c, 0x7c); // jl - next = c->code.length(); - c->code.append(0); - - compare(c, 4, al, 4, bl); - conditional(c, 0x83, target); // jae - break; - - default: - abort(c); - } - - if (next) { - int8_t nextOffset = c->code.length() - next - 1; - c->code.set(next, &nextOffset, 1); - } -} - -void -branchRR(Context* c, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Register* b, - Assembler::Constant* target) -{ - if (isFloatBranch(op)) { - compareFloatRR(c, size, a, size, b); - branchFloat(c, op, target); - } else if (size > TargetBytesPerWord) { - Assembler::Register ah(a->high); - Assembler::Register bh(b->high); - - branchLong(c, op, a, &ah, b, &bh, target, CAST2(compareRR)); - } else { - compareRR(c, size, a, size, b); - branch(c, op, target); - } -} - -void -branchCR(Context* c, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Register* b, - Assembler::Constant* target) -{ - assert(c, not isFloatBranch(op)); - - if (size > TargetBytesPerWord) { - int64_t v = a->value->value(); - - ResolvedPromise low(v & ~static_cast(0)); - Assembler::Constant al(&low); - - ResolvedPromise high((v >> 32) & ~static_cast(0)); - Assembler::Constant ah(&high); - - Assembler::Register bh(b->high); - - branchLong(c, op, &al, &ah, b, &bh, target, CAST2(compareCR)); - } else { - compareCR(c, size, a, size, b); - branch(c, op, target); - } -} - -void -branchRM(Context* c, TernaryOperation op, unsigned size, - Assembler::Register* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(c, not isFloatBranch(op)); - assert(c, size <= TargetBytesPerWord); - - compareRM(c, size, a, size, b); - branch(c, op, target); -} - -void -branchCM(Context* c, TernaryOperation op, unsigned size, - Assembler::Constant* a, Assembler::Memory* b, - Assembler::Constant* target) -{ - assert(c, not isFloatBranch(op)); - assert(c, size <= TargetBytesPerWord); - - compareCM(c, size, a, size, b); - branch(c, op, target); -} - -void -multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - if (TargetBytesPerWord == 4 and aSize == 8) { - const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); - Assembler::Register tmp(c->client->acquireTemporary(mask), - c->client->acquireTemporary(mask)); - - moveCR(c, aSize, a, aSize, &tmp); - multiplyRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - c->client->releaseTemporary(tmp.high); - } else { - int64_t v = a->value->value(); - if (v != 1) { - if (isInt32(v)) { - maybeRex(c, bSize, b, b); - if (isInt8(v)) { - opcode(c, 0x6b); - modrm(c, 0xc0, b, b); - c->code.append(v); - } else { - opcode(c, 0x69); - modrm(c, 0xc0, b, b); - c->code.append4(v); - } - } else { - Assembler::Register tmp - (c->client->acquireTemporary(GeneralRegisterMask)); - moveCR(c, aSize, a, aSize, &tmp); - multiplyRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); - } - } - } -} - -void -divideRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b UNUSED) -{ - assert(c, aSize == bSize); - - assert(c, b->low == rax); - assert(c, a->low != rdx); - - c->client->save(rdx); - - maybeRex(c, aSize, a, b); - opcode(c, 0x99); // cdq - maybeRex(c, aSize, b, a); - opcode(c, 0xf7, 0xf8 + regCode(a)); -} - -void -remainderRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, aSize == bSize); - - assert(c, b->low == rax); - assert(c, a->low != rdx); - - c->client->save(rdx); - - maybeRex(c, aSize, a, b); - opcode(c, 0x99); // cdq - maybeRex(c, aSize, b, a); - opcode(c, 0xf7, 0xf8 + regCode(a)); - - Assembler::Register dx(rdx); - moveRR(c, TargetBytesPerWord, &dx, TargetBytesPerWord, b); -} - -void -doShift(Context* c, UNUSED void (*shift) - (Context*, unsigned, Assembler::Register*, unsigned, - Assembler::Register*), - int type, UNUSED unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - int64_t v = a->value->value(); - - if (TargetBytesPerWord == 4 and bSize == 8) { - c->client->save(rcx); - - Assembler::Register cx(rcx); - ResolvedPromise promise(v & 0x3F); - Assembler::Constant masked(&promise); - moveCR(c, 4, &masked, 4, &cx); - shift(c, aSize, &cx, bSize, b); - } else { - maybeRex(c, bSize, b); - if (v == 1) { - opcode(c, 0xd1, type + regCode(b)); - } else if (isInt8(v)) { - opcode(c, 0xc1, type + regCode(b)); - c->code.append(v); - } else { - abort(c); - } - } -} - -void -shiftLeftRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - if (TargetBytesPerWord == 4 and bSize == 8) { - if (a->low != rcx) { - c->client->save(rcx); - Assembler::Register cx(rcx); - ResolvedPromise promise(0x3F); - Assembler::Constant mask(&promise); - moveRR(c, 4, a, 4, &cx); - andCR(c, 4, &mask, 4, &cx); - } - - // shld - opcode(c, 0x0f, 0xa5); - modrm(c, 0xc0, b->high, b->low); - - // shl - opcode(c, 0xd3, 0xe0 + b->low); - - ResolvedPromise promise(32); - Assembler::Constant constant(&promise); - compareCR(c, aSize, &constant, aSize, a); - - opcode(c, 0x7c); //jl - c->code.append(2 + 2); - - Assembler::Register bh(b->high); - moveRR(c, 4, b, 4, &bh); // 2 bytes - xorRR(c, 4, b, 4, b); // 2 bytes - } else { - assert(c, a->low == rcx); - - maybeRex(c, bSize, a, b); - opcode(c, 0xd3, 0xe0 + regCode(b)); - } -} - -void -shiftLeftCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - doShift(c, shiftLeftRR, 0xe0, aSize, a, bSize, b); -} - -void -shiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - if (TargetBytesPerWord == 4 and bSize == 8) { - if (a->low != rcx) { - c->client->save(rcx); - Assembler::Register cx(rcx); - ResolvedPromise promise(0x3F); - Assembler::Constant mask(&promise); - moveRR(c, 4, a, 4, &cx); - andCR(c, 4, &mask, 4, &cx); - } - - // shrd - opcode(c, 0x0f, 0xad); - modrm(c, 0xc0, b->low, b->high); - - // sar - opcode(c, 0xd3, 0xf8 + b->high); - - ResolvedPromise promise(32); - Assembler::Constant constant(&promise); - compareCR(c, aSize, &constant, aSize, a); - - opcode(c, 0x7c); //jl - c->code.append(2 + 3); - - Assembler::Register bh(b->high); - moveRR(c, 4, &bh, 4, b); // 2 bytes - - // sar 31,high - opcode(c, 0xc1, 0xf8 + b->high); - c->code.append(31); - } else { - assert(c, a->low == rcx); - - maybeRex(c, bSize, a, b); - opcode(c, 0xd3, 0xf8 + regCode(b)); - } -} - -void -shiftRightCR(Context* c, unsigned aSize, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - doShift(c, shiftRightRR, 0xf8, aSize, a, bSize, b); -} - -void -unsignedShiftRightRR(Context* c, UNUSED unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - if (TargetBytesPerWord == 4 and bSize == 8) { - if (a->low != rcx) { - c->client->save(rcx); - Assembler::Register cx(rcx); - ResolvedPromise promise(0x3F); - Assembler::Constant mask(&promise); - moveRR(c, 4, a, 4, &cx); - andCR(c, 4, &mask, 4, &cx); - } - - // shrd - opcode(c, 0x0f, 0xad); - modrm(c, 0xc0, b->low, b->high); - - // shr - opcode(c, 0xd3, 0xe8 + b->high); - - ResolvedPromise promise(32); - Assembler::Constant constant(&promise); - compareCR(c, aSize, &constant, aSize, a); - - opcode(c, 0x7c); //jl - c->code.append(2 + 2); - - Assembler::Register bh(b->high); - moveRR(c, 4, &bh, 4, b); // 2 bytes - xorRR(c, 4, &bh, 4, &bh); // 2 bytes - } else { - assert(c, a->low == rcx); - - maybeRex(c, bSize, a, b); - opcode(c, 0xd3, 0xe8 + regCode(b)); - } -} - -void -unsignedShiftRightCR(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, - unsigned bSize, Assembler::Register* b) -{ - doShift(c, unsignedShiftRightRR, 0xe8, aSize, a, bSize, b); -} - -void -floatRegOp(Context* c, unsigned aSize, Assembler::Register* a, unsigned bSize, - Assembler::Register* b, uint8_t op, uint8_t mod = 0xc0) -{ - if (aSize == 4) { - opcode(c, 0xf3); - } else { - opcode(c, 0xf2); - } - maybeRex(c, bSize, b, a); - opcode(c, 0x0f, op); - modrm(c, mod, a, b); -} - -void -floatMemOp(Context* c, unsigned aSize, Assembler::Memory* a, unsigned bSize, - Assembler::Register* b, uint8_t op) -{ - if (aSize == 4) { - opcode(c, 0xf3); - } else { - opcode(c, 0xf2); - } - maybeRex(c, bSize, b, a); - opcode(c, 0x0f, op); - modrmSibImm(c, b, a); -} - -void -floatSqrtRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x51); -} - -void -floatSqrtMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x51); -} - -void -floatAddRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x58); -} - -void -floatAddMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x58); -} - -void -floatSubtractRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x5c); -} - -void -floatSubtractMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x5c); -} - -void -floatMultiplyRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x59); -} - -void -floatMultiplyMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x59); -} - -void -floatDivideRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x5e); -} - -void -floatDivideMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x5e); -} - -void -float2FloatRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatRegOp(c, aSize, a, 4, b, 0x5a); -} - -void -float2FloatMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, 4, b, 0x5a); -} - -void -float2IntRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - assert(c, not floatReg(b)); - floatRegOp(c, aSize, a, bSize, b, 0x2c); -} - -void -float2IntMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize, Assembler::Register* b) -{ - floatMemOp(c, aSize, a, bSize, b, 0x2c); -} - -void -int2FloatRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize, Assembler::Register* b) -{ - floatRegOp(c, bSize, a, aSize, b, 0x2a); -} - -void -int2FloatMR(Context* c, unsigned aSize, Assembler::Memory* a, - unsigned bSize, Assembler::Register* b) -{ - floatMemOp(c, bSize, a, aSize, b, 0x2a); -} - -void -floatNegateRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, floatReg(a) and floatReg(b)); - // unlike most of the other floating point code, this does NOT - // support doubles: - assert(c, aSize == 4); - ResolvedPromise pcon(0x80000000); - Assembler::Constant con(&pcon); - if (a->low == b->low) { - Assembler::Register tmp(c->client->acquireTemporary(FloatRegisterMask)); - moveCR(c, 4, &con, 4, &tmp); - maybeRex(c, 4, a, &tmp); - opcode(c, 0x0f, 0x57); - modrm(c, 0xc0, &tmp, a); - c->client->releaseTemporary(tmp.low); - } else { - moveCR(c, 4, &con, 4, b); - if (aSize == 8) opcode(c, 0x66); - maybeRex(c, 4, a, b); - opcode(c, 0x0f, 0x57); - modrm(c, 0xc0, a, b); - } -} - -void -floatAbsoluteRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b) -{ - assert(c, floatReg(a) and floatReg(b)); - // unlike most of the other floating point code, this does NOT - // support doubles: - assert(c, aSize == 4); - ResolvedPromise pcon(0x7fffffff); - Assembler::Constant con(&pcon); - if (a->low == b->low) { - Assembler::Register tmp(c->client->acquireTemporary(FloatRegisterMask)); - moveCR(c, 4, &con, 4, &tmp); - maybeRex(c, 4, a, &tmp); - opcode(c, 0x0f, 0x54); - modrm(c, 0xc0, &tmp, a); - c->client->releaseTemporary(tmp.low); - } else { - moveCR(c, 4, &con, 4, b); - maybeRex(c, 4, a, b); - opcode(c, 0x0f, 0x54); - modrm(c, 0xc0, a, b); - } -} - -void -absoluteRR(Context* c, unsigned aSize, Assembler::Register* a, - unsigned bSize UNUSED, Assembler::Register* b UNUSED) -{ - assert(c, aSize == bSize and a->low == rax and b->low == rax); - Assembler::Register d - (c->client->acquireTemporary(static_cast(1) << rdx)); - maybeRex(c, aSize, a, b); - opcode(c, 0x99); - xorRR(c, aSize, &d, aSize, a); - subtractRR(c, aSize, &d, aSize, a); - c->client->releaseTemporary(rdx); -} - -unsigned -argumentFootprint(unsigned footprint) -{ - return max(pad(footprint, StackAlignmentInWords), StackAlignmentInWords); -} - -uint32_t -read4(uint8_t* p) -{ - uint32_t v; memcpy(&v, p, 4); - return v; -} - -void -nextFrame(ArchitectureContext* c UNUSED, uint8_t* start, unsigned size UNUSED, - unsigned footprint, void*, bool mostRecent, - unsigned targetParameterFootprint, void** ip, void** stack) -{ - assert(c, *ip >= start); - assert(c, *ip <= start + size); - - uint8_t* instruction = static_cast(*ip); - - // skip stack overflow check, if present: - if (TargetBytesPerWord == 4) { - if (*start == 0x39) { - start += 12; - } - } else if (*start == 0x48 and start[1] == 0x39) { - start += 13; - } - - if (instruction <= start) { - assert(c, mostRecent); - *ip = static_cast(*stack)[0]; - return; - } - - if (UseFramePointer) { - // skip preamble - start += (TargetBytesPerWord == 4 ? 3 : 4); - - if (instruction <= start or *instruction == 0x5d) { - assert(c, mostRecent); - - *ip = static_cast(*stack)[1]; - *stack = static_cast(*stack) + 1; - return; - } - } - - if (*instruction == 0xc3) { // return - *ip = static_cast(*stack)[0]; - return; - } - - unsigned offset = footprint + FrameHeaderSize - (mostRecent ? 1 : 0); - - if (TailCalls) { - if (argumentFootprint(targetParameterFootprint) > StackAlignmentInWords) { - offset += argumentFootprint(targetParameterFootprint) - - StackAlignmentInWords; - } - - // check for post-non-tail-call stack adjustment of the form "add - // $offset,%rsp": - if (TargetBytesPerWord == 4) { - if ((*instruction == 0x83 or *instruction == 0x81) - and instruction[1] == 0xec) - { - offset - -= (*instruction == 0x83 ? instruction[2] : read4(instruction + 2)) - / TargetBytesPerWord; - } - } else if (*instruction == 0x48 - and (instruction[1] == 0x83 or instruction[1] == 0x81) - and instruction[2] == 0xec) - { - offset - -= (instruction[1] == 0x83 ? instruction[3] : read4(instruction + 3)) - / TargetBytesPerWord; - } - - // todo: check for and handle tail calls - } - - *ip = static_cast(*stack)[offset]; - *stack = static_cast(*stack) + offset; -} - -void -populateTables(ArchitectureContext* c) -{ - const OperandType C = ConstantOperand; - const OperandType A = AddressOperand; - const OperandType R = RegisterOperand; - const OperandType M = MemoryOperand; - - OperationType* zo = c->operations; - UnaryOperationType* uo = c->unaryOperations; - BinaryOperationType* bo = c->binaryOperations; - BranchOperationType* bro = c->branchOperations; - - zo[Return] = return_; - zo[LoadBarrier] = ignore; - zo[StoreStoreBarrier] = ignore; - zo[StoreLoadBarrier] = storeLoadBarrier; - zo[Trap] = trap; - - uo[index(c, Call, C)] = CAST1(callC); - uo[index(c, Call, R)] = CAST1(callR); - uo[index(c, Call, M)] = CAST1(callM); - - uo[index(c, AlignedCall, C)] = CAST1(alignedCallC); - - uo[index(c, LongCall, C)] = CAST1(longCallC); - - uo[index(c, AlignedLongCall, C)] = CAST1(alignedLongCallC); - - uo[index(c, Jump, R)] = CAST1(jumpR); - uo[index(c, Jump, C)] = CAST1(jumpC); - uo[index(c, Jump, M)] = CAST1(jumpM); - - uo[index(c, AlignedJump, C)] = CAST1(alignedJumpC); - - uo[index(c, LongJump, C)] = CAST1(longJumpC); - - uo[index(c, AlignedLongJump, C)] = CAST1(alignedLongJumpC); - - bo[index(c, Negate, R, R)] = CAST2(negateRR); - - bo[index(c, FloatNegate, R, R)] = CAST2(floatNegateRR); - - bo[index(c, Move, R, R)] = CAST2(moveRR); - bo[index(c, Move, C, R)] = CAST2(moveCR); - bo[index(c, Move, M, R)] = CAST2(moveMR); - bo[index(c, Move, R, M)] = CAST2(moveRM); - bo[index(c, Move, C, M)] = CAST2(moveCM); - bo[index(c, Move, A, R)] = CAST2(moveAR); - - bo[index(c, FloatSquareRoot, R, R)] = CAST2(floatSqrtRR); - bo[index(c, FloatSquareRoot, M, R)] = CAST2(floatSqrtMR); - - bo[index(c, MoveZ, R, R)] = CAST2(moveZRR); - bo[index(c, MoveZ, M, R)] = CAST2(moveZMR); - bo[index(c, MoveZ, C, R)] = CAST2(moveCR); - - bo[index(c, Add, R, R)] = CAST2(addRR); - bo[index(c, Add, C, R)] = CAST2(addCR); - - bo[index(c, Subtract, C, R)] = CAST2(subtractCR); - bo[index(c, Subtract, R, R)] = CAST2(subtractRR); - - bo[index(c, FloatAdd, R, R)] = CAST2(floatAddRR); - bo[index(c, FloatAdd, M, R)] = CAST2(floatAddMR); - - bo[index(c, FloatSubtract, R, R)] = CAST2(floatSubtractRR); - bo[index(c, FloatSubtract, M, R)] = CAST2(floatSubtractMR); - - bo[index(c, And, R, R)] = CAST2(andRR); - bo[index(c, And, C, R)] = CAST2(andCR); - - bo[index(c, Or, R, R)] = CAST2(orRR); - bo[index(c, Or, C, R)] = CAST2(orCR); - - bo[index(c, Xor, R, R)] = CAST2(xorRR); - bo[index(c, Xor, C, R)] = CAST2(xorCR); - - bo[index(c, Multiply, R, R)] = CAST2(multiplyRR); - bo[index(c, Multiply, C, R)] = CAST2(multiplyCR); - - bo[index(c, Divide, R, R)] = CAST2(divideRR); - - bo[index(c, FloatMultiply, R, R)] = CAST2(floatMultiplyRR); - bo[index(c, FloatMultiply, M, R)] = CAST2(floatMultiplyMR); - - bo[index(c, FloatDivide, R, R)] = CAST2(floatDivideRR); - bo[index(c, FloatDivide, M, R)] = CAST2(floatDivideMR); - - bo[index(c, Remainder, R, R)] = CAST2(remainderRR); - - bo[index(c, ShiftLeft, R, R)] = CAST2(shiftLeftRR); - bo[index(c, ShiftLeft, C, R)] = CAST2(shiftLeftCR); - - bo[index(c, ShiftRight, R, R)] = CAST2(shiftRightRR); - bo[index(c, ShiftRight, C, R)] = CAST2(shiftRightCR); - - bo[index(c, UnsignedShiftRight, R, R)] = CAST2(unsignedShiftRightRR); - bo[index(c, UnsignedShiftRight, C, R)] = CAST2(unsignedShiftRightCR); - - bo[index(c, Float2Float, R, R)] = CAST2(float2FloatRR); - bo[index(c, Float2Float, M, R)] = CAST2(float2FloatMR); - - bo[index(c, Float2Int, R, R)] = CAST2(float2IntRR); - bo[index(c, Float2Int, M, R)] = CAST2(float2IntMR); - - bo[index(c, Int2Float, R, R)] = CAST2(int2FloatRR); - bo[index(c, Int2Float, M, R)] = CAST2(int2FloatMR); - - bo[index(c, Absolute, R, R)] = CAST2(absoluteRR); - bo[index(c, FloatAbsolute, R, R)] = CAST2(floatAbsoluteRR); - - bro[branchIndex(c, R, R)] = CAST_BRANCH(branchRR); - bro[branchIndex(c, C, R)] = CAST_BRANCH(branchCR); - bro[branchIndex(c, C, M)] = CAST_BRANCH(branchCM); - bro[branchIndex(c, R, M)] = CAST_BRANCH(branchRM); -} - -class MyArchitecture: public Assembler::Architecture { - public: - MyArchitecture(System* system, bool useNativeFeatures): - c(system, useNativeFeatures), referenceCount(0) - { - populateTables(&c); - } - - virtual unsigned floatRegisterSize() { - if (useSSE(&c)) { - return 8; - } else { - return 0; - } - } - - virtual uint32_t generalRegisterMask() { - return GeneralRegisterMask; - } - - virtual uint32_t floatRegisterMask() { - return useSSE(&c) ? FloatRegisterMask : 0; - } - - virtual int scratch() { - return rax; - } - - virtual int stack() { - return rsp; - } - - virtual int thread() { - return rbx; - } - - virtual int returnLow() { - return rax; - } - - virtual int returnHigh() { - return (TargetBytesPerWord == 4 ? rdx : NoRegister); - } - - virtual int virtualCallTarget() { - return rax; - } - - virtual int virtualCallIndex() { - return rdx; - } - - virtual bool bigEndian() { - return false; - } - - virtual uintptr_t maximumImmediateJump() { - return 0x7FFFFFFF; - } - - virtual bool reserved(int register_) { - switch (register_) { - case rbp: - return UseFramePointer; - - case rsp: - case rbx: - return true; - - default: - return false; - } - } - - virtual unsigned frameFootprint(unsigned footprint) { -#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE - return max(footprint, StackAlignmentInWords); -#else - return max(footprint > argumentRegisterCount() ? - footprint - argumentRegisterCount() : 0, - StackAlignmentInWords); -#endif - } - - virtual unsigned argumentFootprint(unsigned footprint) { - return local::argumentFootprint(footprint); - } - - virtual bool argumentAlignment() { - return false; - } - - virtual bool argumentRegisterAlignment() { - return false; - } - - virtual unsigned argumentRegisterCount() { -#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE - if (TargetBytesPerWord == 8) return 4; else -#else - if (TargetBytesPerWord == 8) return 6; else -#endif - return 0; - } - - virtual int argumentRegister(unsigned index) { - assert(&c, TargetBytesPerWord == 8); - switch (index) { -#if AVIAN_TARGET_FORMAT == AVIAN_FORMAT_PE - case 0: - return rcx; - case 1: - return rdx; - case 2: - return r8; - case 3: - return r9; -#else - case 0: - return rdi; - case 1: - return rsi; - case 2: - return rdx; - case 3: - return rcx; - case 4: - return r8; - case 5: - return r9; -#endif - default: - abort(&c); - } - } - - virtual bool hasLinkRegister() { - return false; - } - - 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, void* returnAddress, - void* newTarget) - { - bool assertAlignment UNUSED; - switch (op) { - case AlignedCall: - op = Call; - assertAlignment = true; - break; - - case AlignedJump: - op = Jump; - assertAlignment = true; - break; - - case AlignedLongCall: - op = LongCall; - assertAlignment = true; - break; - - case AlignedLongJump: - op = LongJump; - assertAlignment = true; - break; - - default: - assertAlignment = false; - } - - if (TargetBytesPerWord == 4 or op == Call or op == Jump) { - uint8_t* instruction = static_cast(returnAddress) - 5; - - assert(&c, ((op == Call or op == LongCall) and *instruction == 0xE8) - or ((op == Jump or op == LongJump) and *instruction == 0xE9)); - - assert(&c, (not assertAlignment) - or reinterpret_cast(instruction + 1) % 4 == 0); - - intptr_t v = static_cast(newTarget) - - static_cast(returnAddress); - - assert(&c, isInt32(v)); - - int32_t v32 = v; - - memcpy(instruction + 1, &v32, 4); - } else { - uint8_t* instruction = static_cast(returnAddress) - 13; - - assert(&c, instruction[0] == 0x49 and instruction[1] == 0xBA); - assert(&c, instruction[10] == 0x41 and instruction[11] == 0xFF); - assert(&c, (op == LongCall and instruction[12] == 0xD2) - or (op == LongJump and instruction[12] == 0xE2)); - - assert(&c, (not assertAlignment) - or reinterpret_cast(instruction + 2) % 8 == 0); - - memcpy(instruction + 2, &newTarget, 8); - } - } - - virtual void setConstant(void* dst, uint64_t constant) { - target_uintptr_t v = targetVW(constant); - memcpy(dst, &v, TargetBytesPerWord); - } - - virtual unsigned alignFrameSize(unsigned sizeInWords) { - return pad(sizeInWords + FrameHeaderSize, StackAlignmentInWords) - - FrameHeaderSize; - } - - virtual void nextFrame(void* start, unsigned size, unsigned footprint, - void* link, bool mostRecent, - unsigned targetParameterFootprint, void** ip, - void** stack) - { - local::nextFrame(&c, static_cast(start), size, footprint, - link, mostRecent, targetParameterFootprint, ip, stack); - } - - virtual void* frameIp(void* stack) { - return stack ? *static_cast(stack) : 0; - } - - virtual unsigned frameHeaderSize() { - return FrameHeaderSize; - } - - virtual unsigned frameReturnAddressSize() { - return 1; - } - - virtual unsigned frameFooterSize() { - return 0; - } - - virtual bool alwaysCondensed(BinaryOperation op) { - switch(op) { - case Float2Float: - case Float2Int: - case Int2Float: - case FloatAbsolute: - case FloatNegate: - case FloatSquareRoot: - return false; - - case Negate: - case Absolute: - return true; - - default: - abort(&c); - } - } - - virtual bool alwaysCondensed(TernaryOperation) { - return true; - } - - virtual int returnAddressOffset() { - return 0; - } - - virtual int framePointerOffset() { - return UseFramePointer ? -1 : 0; - } - - virtual void plan - (UnaryOperation, - unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask, - bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand) - | (1 << ConstantOperand); - *aRegisterMask = ~static_cast(0); - *thunk = false; - } - - virtual void planSource - (BinaryOperation op, - unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask, - unsigned bSize, bool* thunk) - { - *aTypeMask = ~0; - *aRegisterMask = GeneralRegisterMask | - (static_cast(GeneralRegisterMask) << 32); - - *thunk = false; - - switch (op) { - case Negate: - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = (static_cast(1) << (rdx + 32)) - | (static_cast(1) << rax); - break; - - case Absolute: - if (aSize <= TargetBytesPerWord) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = (static_cast(1) << rax); - } else { - *thunk = true; - } - break; - - case FloatAbsolute: - if (useSSE(&c)) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - } else { - *thunk = true; - } - break; - - case FloatNegate: - // floatNegateRR does not support doubles - if (useSSE(&c) and aSize == 4 and bSize == 4) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = FloatRegisterMask; - } else { - *thunk = true; - } - break; - - case FloatSquareRoot: - if (useSSE(&c)) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *aRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - } else { - *thunk = true; - } - break; - - case Float2Float: - if (useSSE(&c)) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *aRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - } else { - *thunk = true; - } - break; - - case Float2Int: - // todo: Java requires different semantics than SSE for - // converting floats to integers, we we need to either use - // thunks or produce inline machine code which handles edge - // cases properly. - if (false and useSSE(&c) and bSize <= TargetBytesPerWord) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *aRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - } else { - *thunk = true; - } - break; - - case Int2Float: - if (useSSE(&c) and aSize <= TargetBytesPerWord) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *aRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - } else { - *thunk = true; - } - break; - - case Move: - *aTypeMask = ~0; - *aRegisterMask = ~static_cast(0); - - if (TargetBytesPerWord == 4) { - if (aSize == 4 and bSize == 8) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - const uint32_t mask - = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); - *aRegisterMask = (static_cast(mask) << 32) | mask; - } else if (aSize == 1 or bSize == 1) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - const uint32_t mask - = (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx); - *aRegisterMask = (static_cast(mask) << 32) | mask; - } - } - break; - - default: - break; - } - } - - virtual void planDestination - (BinaryOperation op, unsigned aSize, uint8_t aTypeMask, - uint64_t aRegisterMask, unsigned bSize, uint8_t* bTypeMask, - uint64_t* bRegisterMask) - { - *bTypeMask = ~0; - *bRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - - switch (op) { - case Absolute: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = (static_cast(1) << rax); - break; - - case FloatAbsolute: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = aRegisterMask; - break; - - case Negate: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = aRegisterMask; - break; - - case FloatNegate: - case FloatSquareRoot: - case Float2Float: - case Int2Float: - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - break; - - case Float2Int: - *bTypeMask = (1 << RegisterOperand); - break; - - case Move: - if (aTypeMask & ((1 << MemoryOperand) | 1 << AddressOperand)) { - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32) - | FloatRegisterMask; - } else if (aTypeMask & (1 << RegisterOperand)) { - *bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - if (aRegisterMask & FloatRegisterMask) { - *bRegisterMask = FloatRegisterMask; - } else { - *bRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - } - } else { - *bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - } - - if (TargetBytesPerWord == 4) { - if (aSize == 4 and bSize == 8) { - *bRegisterMask = (static_cast(1) << (rdx + 32)) - | (static_cast(1) << rax); - } else if (aSize == 1 or bSize == 1) { - const uint32_t mask - = (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx); - *bRegisterMask = (static_cast(mask) << 32) | mask; - } - } - break; - - default: - break; - } - } - - virtual void planMove - (unsigned size, uint8_t* srcTypeMask, uint64_t* srcRegisterMask, - uint8_t* tmpTypeMask, uint64_t* tmpRegisterMask, - uint8_t dstTypeMask, uint64_t dstRegisterMask) - { - *srcTypeMask = ~0; - *srcRegisterMask = ~static_cast(0); - - *tmpTypeMask = 0; - *tmpRegisterMask = 0; - - if (dstTypeMask & (1 << MemoryOperand)) { - // can't move directly from memory to memory - *srcTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *tmpTypeMask = 1 << RegisterOperand; - *tmpRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - } else if (dstTypeMask & (1 << RegisterOperand)) { - if (size > TargetBytesPerWord) { - // can't move directly from FPR to GPR or vice-versa for - // values larger than the GPR size - if (dstRegisterMask & FloatRegisterMask) { - *srcRegisterMask = FloatRegisterMask - | (static_cast(FloatRegisterMask) << 32); - *tmpTypeMask = 1 << MemoryOperand; - } else if (dstRegisterMask & GeneralRegisterMask) { - *srcRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - *tmpTypeMask = 1 << MemoryOperand; - } - } - if (dstRegisterMask & FloatRegisterMask) { - // can't move directly from constant to FPR - *srcTypeMask &= ~(1 << ConstantOperand); - if (size > TargetBytesPerWord) { - *tmpTypeMask = 1 << MemoryOperand; - } else { - *tmpTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *tmpRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - } - } - } - } - - virtual void planSource - (TernaryOperation op, - unsigned aSize, uint8_t *aTypeMask, uint64_t *aRegisterMask, - unsigned bSize, uint8_t* bTypeMask, uint64_t* bRegisterMask, - unsigned, bool* thunk) - { - *aTypeMask = (1 << RegisterOperand) | (1 << ConstantOperand); - *aRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - - *bTypeMask = (1 << RegisterOperand); - *bRegisterMask = GeneralRegisterMask - | (static_cast(GeneralRegisterMask) << 32); - - *thunk = false; - - switch (op) { - case FloatAdd: - case FloatSubtract: - case FloatMultiply: - case FloatDivide: - if (useSSE(&c)) { - *aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand); - *bTypeMask = (1 << RegisterOperand); - - const uint64_t mask - = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - *aRegisterMask = mask; - *bRegisterMask = mask; - } else { - *thunk = true; - } - break; - - case FloatRemainder: - *thunk = true; - break; - - case Multiply: - if (TargetBytesPerWord == 4 and aSize == 8) { - const uint32_t mask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); - *aRegisterMask = (static_cast(mask) << 32) | mask; - *bRegisterMask = (static_cast(1) << (rdx + 32)) | mask; - } else { - *aRegisterMask = GeneralRegisterMask; - *bRegisterMask = GeneralRegisterMask; - } - break; - - case Divide: - if (TargetBytesPerWord == 4 and aSize == 8) { - *thunk = true; - } else { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); - *bRegisterMask = 1 << rax; - } - break; - - case Remainder: - if (TargetBytesPerWord == 4 and aSize == 8) { - *thunk = true; - } else { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = GeneralRegisterMask & ~((1 << rax) | (1 << rdx)); - *bRegisterMask = 1 << rax; - } - break; - - case ShiftLeft: - case ShiftRight: - case UnsignedShiftRight: { - if (TargetBytesPerWord == 4 and bSize == 8) { - const uint32_t mask = GeneralRegisterMask & ~(1 << rcx); - *aRegisterMask = (static_cast(mask) << 32) | mask; - *bRegisterMask = (static_cast(mask) << 32) | mask; - } else { - *aRegisterMask = (static_cast(GeneralRegisterMask) << 32) - | (static_cast(1) << rcx); - const uint32_t mask = GeneralRegisterMask & ~(1 << rcx); - *bRegisterMask = (static_cast(mask) << 32) | mask; - } - } break; - - case JumpIfFloatEqual: - case JumpIfFloatNotEqual: - case JumpIfFloatLess: - case JumpIfFloatGreater: - case JumpIfFloatLessOrEqual: - case JumpIfFloatGreaterOrEqual: - case JumpIfFloatLessOrUnordered: - case JumpIfFloatGreaterOrUnordered: - case JumpIfFloatLessOrEqualOrUnordered: - case JumpIfFloatGreaterOrEqualOrUnordered: - if (useSSE(&c)) { - *aTypeMask = (1 << RegisterOperand); - *aRegisterMask = (static_cast(FloatRegisterMask) << 32) - | FloatRegisterMask; - *bTypeMask = *aTypeMask; - *bRegisterMask = *aRegisterMask; - } else { - *thunk = true; - } - break; - - default: - break; - } - } - - virtual void planDestination - (TernaryOperation op, unsigned, uint8_t, uint64_t, unsigned, uint8_t, - uint64_t bRegisterMask, unsigned, uint8_t* cTypeMask, - uint64_t* cRegisterMask) - { - if (isBranch(op)) { - *cTypeMask = (1 << ConstantOperand); - *cRegisterMask = 0; - } else { - *cTypeMask = (1 << RegisterOperand); - *cRegisterMask = bRegisterMask; - } - } - - virtual void acquire() { - ++ referenceCount; - } - - virtual void release() { - if (-- referenceCount == 0) { - c.s->free(this); - } - } - - ArchitectureContext c; - unsigned referenceCount; -}; - -class MyAssembler: public Assembler { - public: - MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch): - c(s, a, zone, &(arch->c)), arch_(arch) - { } - - virtual void setClient(Client* client) { - assert(&c, c.client == 0); - c.client = client; - } - - virtual Architecture* arch() { - return arch_; - } - - virtual void checkStackOverflow(uintptr_t handler, - unsigned stackLimitOffsetFromThread) - { - Register stack(rsp); - Memory stackLimit(rbx, stackLimitOffsetFromThread); - Constant handlerConstant(resolved(&c, handler)); - branchRM(&c, JumpIfGreaterOrEqual, TargetBytesPerWord, &stack, &stackLimit, - &handlerConstant); - } - - virtual void saveFrame(unsigned stackOffset, unsigned) { - Register stack(rsp); - Memory stackDst(rbx, stackOffset); - apply(Move, TargetBytesPerWord, RegisterOperand, &stack, - TargetBytesPerWord, MemoryOperand, &stackDst); - } - - virtual void pushFrame(unsigned argumentCount, ...) { - struct Argument { - unsigned size; - OperandType type; - Operand* operand; - }; - RUNTIME_ARRAY(Argument, arguments, argumentCount); - va_list a; va_start(a, argumentCount); - unsigned footprint = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - RUNTIME_ARRAY_BODY(arguments)[i].size = va_arg(a, unsigned); - RUNTIME_ARRAY_BODY(arguments)[i].type - = static_cast(va_arg(a, int)); - RUNTIME_ARRAY_BODY(arguments)[i].operand = va_arg(a, Operand*); - footprint += ceiling - (RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); - } - va_end(a); - - allocateFrame(arch_->alignFrameSize(footprint)); - - unsigned offset = 0; - for (unsigned i = 0; i < argumentCount; ++i) { - if (i < arch_->argumentRegisterCount()) { - Register dst(arch_->argumentRegister(i)); - apply(Move, - RUNTIME_ARRAY_BODY(arguments)[i].size, - RUNTIME_ARRAY_BODY(arguments)[i].type, - RUNTIME_ARRAY_BODY(arguments)[i].operand, - pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), - RegisterOperand, - &dst); - } else { - Memory dst(rsp, offset * TargetBytesPerWord); - apply(Move, - RUNTIME_ARRAY_BODY(arguments)[i].size, - RUNTIME_ARRAY_BODY(arguments)[i].type, - RUNTIME_ARRAY_BODY(arguments)[i].operand, - pad(RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord), - MemoryOperand, - &dst); - offset += ceiling - (RUNTIME_ARRAY_BODY(arguments)[i].size, TargetBytesPerWord); - } - } - } - - virtual void allocateFrame(unsigned footprint) { - Register stack(rsp); - - if (UseFramePointer) { - Register base(rbp); - pushR(&c, TargetBytesPerWord, &base); - - apply(Move, TargetBytesPerWord, RegisterOperand, &stack, - TargetBytesPerWord, RegisterOperand, &base); - } - - Constant footprintConstant(resolved(&c, footprint * TargetBytesPerWord)); - apply(Subtract, TargetBytesPerWord, ConstantOperand, &footprintConstant, - TargetBytesPerWord, RegisterOperand, &stack, - TargetBytesPerWord, RegisterOperand, &stack); - } - - virtual void adjustFrame(unsigned difference) { - Register stack(rsp); - Constant differenceConstant(resolved(&c, difference * TargetBytesPerWord)); - apply(Subtract, TargetBytesPerWord, ConstantOperand, &differenceConstant, - TargetBytesPerWord, RegisterOperand, &stack, - TargetBytesPerWord, RegisterOperand, &stack); - } - - virtual void popFrame(unsigned frameFootprint) { - if (UseFramePointer) { - Register base(rbp); - Register stack(rsp); - apply(Move, TargetBytesPerWord, RegisterOperand, &base, - TargetBytesPerWord, RegisterOperand, &stack); - - popR(&c, TargetBytesPerWord, &base); - } else { - Register stack(rsp); - Constant footprint(resolved(&c, frameFootprint * TargetBytesPerWord)); - apply(Add, TargetBytesPerWord, ConstantOperand, &footprint, - TargetBytesPerWord, RegisterOperand, &stack, - TargetBytesPerWord, RegisterOperand, &stack); - } - } - - virtual void popFrameForTailCall(unsigned frameFootprint, - int offset, - int returnAddressSurrogate, - int framePointerSurrogate) - { - if (TailCalls) { - if (offset) { - Register tmp(c.client->acquireTemporary()); - - unsigned baseSize = UseFramePointer ? 1 : 0; - - Memory returnAddressSrc - (rsp, (frameFootprint + baseSize) * TargetBytesPerWord); - moveMR(&c, TargetBytesPerWord, &returnAddressSrc, TargetBytesPerWord, - &tmp); - - Memory returnAddressDst - (rsp, (frameFootprint - offset + baseSize) * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &tmp, TargetBytesPerWord, - &returnAddressDst); - - c.client->releaseTemporary(tmp.low); - - if (UseFramePointer) { - Memory baseSrc(rsp, frameFootprint * TargetBytesPerWord); - Register base(rbp); - moveMR(&c, TargetBytesPerWord, &baseSrc, TargetBytesPerWord, &base); - } - - Register stack(rsp); - Constant footprint - (resolved - (&c, (frameFootprint - offset + baseSize) * TargetBytesPerWord)); - - addCR(&c, TargetBytesPerWord, &footprint, TargetBytesPerWord, &stack); - - if (returnAddressSurrogate != NoRegister) { - assert(&c, offset > 0); - - Register ras(returnAddressSurrogate); - Memory dst(rsp, offset * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &ras, TargetBytesPerWord, &dst); - } - - if (framePointerSurrogate != NoRegister) { - assert(&c, offset > 0); - - Register fps(framePointerSurrogate); - Memory dst(rsp, (offset - 1) * TargetBytesPerWord); - moveRM(&c, TargetBytesPerWord, &fps, TargetBytesPerWord, &dst); - } - } else { - popFrame(frameFootprint); - } - } else { - abort(&c); - } - } - - virtual void popFrameAndPopArgumentsAndReturn(unsigned frameFootprint, - unsigned argumentFootprint) - { - popFrame(frameFootprint); - - assert(&c, argumentFootprint >= StackAlignmentInWords); - assert(&c, (argumentFootprint % StackAlignmentInWords) == 0); - - if (TailCalls and argumentFootprint > StackAlignmentInWords) { - Register returnAddress(rcx); - popR(&c, TargetBytesPerWord, &returnAddress); - - Register stack(rsp); - Constant adjustment - (resolved(&c, (argumentFootprint - StackAlignmentInWords) - * TargetBytesPerWord)); - addCR(&c, TargetBytesPerWord, &adjustment, TargetBytesPerWord, &stack); - - jumpR(&c, TargetBytesPerWord, &returnAddress); - } else { - return_(&c); - } - } - - virtual void popFrameAndUpdateStackAndReturn(unsigned frameFootprint, - unsigned stackOffsetFromThread) - { - popFrame(frameFootprint); - - Register returnAddress(rcx); - popR(&c, TargetBytesPerWord, &returnAddress); - - Register stack(rsp); - Memory stackSrc(rbx, stackOffsetFromThread); - moveMR(&c, TargetBytesPerWord, &stackSrc, TargetBytesPerWord, &stack); - - jumpR(&c, TargetBytesPerWord, &returnAddress); - } - - virtual void apply(Operation op) { - arch_->c.operations[op](&c); - } - - virtual void apply(UnaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand) - { - arch_->c.unaryOperations[index(&(arch_->c), op, aType)] - (&c, aSize, aOperand); - } - - virtual void apply(BinaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand) - { - arch_->c.binaryOperations[index(&(arch_->c), op, aType, bType)] - (&c, aSize, aOperand, bSize, bOperand); - } - - virtual void apply(TernaryOperation op, - unsigned aSize, OperandType aType, Operand* aOperand, - unsigned bSize, OperandType bType, Operand* bOperand, - unsigned cSize UNUSED, OperandType cType UNUSED, - Operand* cOperand) - { - if (isBranch(op)) { - assert(&c, aSize == bSize); - assert(&c, cSize == TargetBytesPerWord); - assert(&c, cType == ConstantOperand); - - arch_->c.branchOperations[branchIndex(&(arch_->c), aType, bType)] - (&c, op, aSize, aOperand, bOperand, cOperand); - } else { - assert(&c, bSize == cSize); - assert(&c, bType == cType); - - arch_->c.binaryOperations[index(&(arch_->c), op, aType, bType)] - (&c, aSize, aOperand, bSize, bOperand); - } - } - - virtual void setDestination(uint8_t* dst) { - c.result = dst; - } - - virtual void write() { - uint8_t* dst = c.result; - for (MyBlock* b = c.firstBlock; b; b = b->next) { - unsigned index = 0; - unsigned padding = 0; - for (AlignmentPadding* p = b->firstPadding; p; p = p->next) { - unsigned size = p->offset - b->offset - index; - - memcpy(dst + b->start + index + padding, - c.code.data + b->offset + index, - size); - - index += size; - - while ((b->start + index + padding + p->instructionOffset) - % p->alignment) - { - *(dst + b->start + index + padding) = 0x90; - ++ padding; - } - } - - memcpy(dst + b->start + index + padding, - c.code.data + b->offset + index, - b->size - index); - } - - for (Task* t = c.tasks; t; t = t->next) { - t->run(&c); - } - } - - virtual Promise* offset(bool) { - return local::offset(&c); - } - - virtual Block* endBlock(bool startNew) { - MyBlock* b = c.lastBlock; - b->size = c.code.length() - b->offset; - if (startNew) { - c.lastBlock = new(c.zone) MyBlock(c.code.length()); - } else { - c.lastBlock = 0; - } - return b; - } - - virtual void endEvent() { - // ignore - } - - virtual unsigned length() { - return c.code.length(); - } - - virtual unsigned footerSize() { - return 0; - } - - virtual void dispose() { - c.code.dispose(); - } - - Context c; - MyArchitecture* arch_; -}; - -} // namespace local - -} // namespace - -namespace vm { - -Assembler::Architecture* -makeArchitecture(System* system, bool useNativeFeatures) -{ - return new (allocate(system, sizeof(local::MyArchitecture))) - local::MyArchitecture(system, useNativeFeatures); -} - -Assembler* -makeAssembler(System* system, Allocator* allocator, Zone* zone, - Assembler::Architecture* architecture) -{ - return - new(zone) local::MyAssembler(system, allocator, zone, - static_cast(architecture)); -} - -} // namespace vm diff --git a/src/x86.masm b/src/x86.masm new file mode 100644 index 0000000000..2f9dedcbd2 --- /dev/null +++ b/src/x86.masm @@ -0,0 +1,168 @@ +comment # + Copyright (c) 2008-2011, 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. + + ORIGIN: https://github.com/gkvas/avian/tree/wince +# + +.586 +.MODEL FLAT, C + +VOID_TYPE equ 0 +INT8_TYPE equ 1 +INT16_TYPE equ 2 +INT32_TYPE equ 3 +INT64_TYPE equ 4 +FLOAT_TYPE equ 5 +DOUBLE_TYPE equ 6 +POINTER_TYPE equ 7 + +CHECKPOINT_THREAD equ 4 +CHECKPOINT_STACK equ 24 +CHECKPOINT_BASE equ 28 + +_TEXT SEGMENT + +public C detectFeature +detectFeature: + push ebp + mov ebp,esp + push edx + push ecx + push ebx + push esi + push edi + mov esi,ds:dword ptr[12+ebp] + mov edi,ds:dword ptr[8+ebp] + mov eax,1 + cpuid + and edx,esi + and ecx,edi + or ecx,edx + test ecx,ecx + je LNOSSE + mov eax,1 + jmp LSSEEND + +LNOSSE: + mov eax,0 + +LSSEEND: + pop edi + pop esi + pop ebx + pop ecx + pop edx + mov esp,ebp + pop ebp + ret + +public C vmNativeCall +vmNativeCall: + push ebp + mov ebp,esp + mov ecx,ds:dword ptr[16+ebp] + sub esp,ecx + mov ecx,0 + jmp Ltest + +Lloop: + mov eax,ecx + mov edx,ecx + add edx,esp + add eax,ds:dword ptr[12+ebp] + mov eax,ds:dword ptr[eax] + mov ds:dword ptr[edx],eax + add ecx,4 + +Ltest: + cmp ecx,ds:dword ptr[16+ebp] + jb Lloop + call dword ptr[8+ebp] + mov ecx,ds:dword ptr[20+ebp] + +Lvoid: + cmp ecx,offset VOID_TYPE + jne Lint64 + jmp Lexit + +Lint64: + cmp ecx,offset INT64_TYPE + jne Lfloat + jmp Lexit + +Lfloat: + cmp ecx,offset FLOAT_TYPE + jne Ldouble + fstp ds:dword ptr[8+ebp] + mov eax,ds:dword ptr[8+ebp] + jmp Lexit + +Ldouble: + cmp ecx,offset DOUBLE_TYPE + jne Lexit + fstp ds:qword ptr[8+ebp] + mov eax,ds:dword ptr[8+ebp] + mov edx,ds:dword ptr[12+ebp] + +Lexit: + mov esp,ebp + pop ebp + ret + +public C vmJump +vmJump: + mov esi,ds:dword ptr[4+esp] + mov ebp,ds:dword ptr[8+esp] + mov ebx,ds:dword ptr[16+esp] + mov eax,ds:dword ptr[20+esp] + mov edx,ds:dword ptr[24+esp] + mov esp,ds:dword ptr[12+esp] + jmp esi + +VMRUN_FRAME_SIZE equ 24 + +public C vmRun_ +vmRun_: + ; 8(%ebp): function + ; 12(%ebp): arguments + ; 16(%ebp): checkpoint + push ebp + mov ebp,esp + sub esp,offset VMRUN_FRAME_SIZE + + mov ds:dword ptr[8+esp],ebx + mov ds:dword ptr[12+esp],esi + mov ds:dword ptr[16+esp],edi + + mov eax,ds:dword ptr[12+ebp] + mov ds:dword ptr[4+esp],eax + + mov ecx,ds:dword ptr[16+ebp] + mov eax,ds:dword ptr[CHECKPOINT_THREAD+ecx] + mov ds:dword ptr[0+esp],eax + + mov ds:dword ptr[CHECKPOINT_STACK+ecx],esp + + call dword ptr[8+ebp] + +public C vmRun_returnAddress +vmRun_returnAddress: + + mov ebx,ds:dword ptr[8+esp] + mov esi,ds:dword ptr[12+esp] + mov edi,ds:dword ptr[16+esp] + + add esp,offset VMRUN_FRAME_SIZE + pop ebp + ret + +_TEXT ENDS +END \ No newline at end of file diff --git a/test/Floats.java b/test/Floats.java index b8b80cbd75..eca59b1bc9 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -329,5 +329,23 @@ public class Floats { { float v = Float.POSITIVE_INFINITY; expect(Long.MAX_VALUE == (long) v); } + + expect(Double.NaN != Double.NaN); + expect(! (Double.NaN == Double.NaN)); + + { double d = Double.NaN; + expect(Double.NaN != d); + expect(! (Double.NaN == d)); + expect(d != d); + expect(! (d == d)); } + + expect(Float.NaN != Float.NaN); + expect(! (Float.NaN == Float.NaN)); + + { float d = Float.NaN; + expect(Float.NaN != d); + expect(! (Float.NaN == d)); + expect(d != d); + expect(! (d == d)); } } } diff --git a/test/JNI.java b/test/JNI.java index dc24da9b8f..955bfc9052 100644 --- a/test/JNI.java +++ b/test/JNI.java @@ -1,3 +1,7 @@ +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; + public class JNI { static { System.loadLibrary("test"); @@ -7,6 +11,18 @@ public class JNI { if (! v) throw new RuntimeException(); } + private static float echo(float f) { + return f; + } + + private static native float doEcho(float f); + + private static double echo(double f) { + return f; + } + + private static native double doEcho(double f); + private static native double addDoubles (double a1, double a2, double a3, double a4, double a5, double a6, double a7, double a8, double a9, double a10, double a11, double a12, @@ -25,7 +41,29 @@ public class JNI { float a13, float a14, float a15, double a16, float a17, float a18, float a19, float a20); - public static void main(String[] args) { + private static native long fromReflectedMethod(Object m); + + private static native Object toReflectedMethod(Class c, long id, + boolean isStatic); + + private static native int callStaticIntMethod(Class c, long id); + + private static native Object newObject(Class c, long id); + + private static native long fromReflectedField(Field f); + + private static native Field toReflectedField(Class c, long id, + boolean isStatic); + + private static native int getStaticIntField(Class c, long id); + + private static native Object testLocalRef(Object o); + + public static int method242() { return 242; } + + public static final int field950 = 950; + + public static void main(String[] args) throws Exception { expect(addDoubles (1.0d, 2.0d, 3.0d, 4.0d, 5.0d, 6.0d, 7.0d, 8.0d, 9.0d, 10.0d, 11.0d, 12.0d, 13.0d, 14.0d, 15.0d, 16.0d, 17.0d, 18.0d, 19.0d, 20.0d) @@ -40,5 +78,39 @@ public class JNI { (1.0f, 2.0d, 3.0f, 4.0d, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0d, 17.0f, 18.0f, 19.0f, 20.0f) == 210.0d); + + expect(doEcho(42.0f) == 42.0f); + expect(doEcho(42.0d) == 42.0d); + + expect(callStaticIntMethod + (JNI.class, fromReflectedMethod + (JNI.class.getMethod("method242"))) == 242); + + expect(((Method) toReflectedMethod + (JNI.class, fromReflectedMethod + (JNI.class.getMethod("method242")), true)) + .getName().equals("method242")); + + expect(newObject + (JNI.class, fromReflectedMethod + (JNI.class.getConstructor())) instanceof JNI); + + expect(((Constructor) toReflectedMethod + (JNI.class, fromReflectedMethod + (JNI.class.getConstructor()), false)) + .getDeclaringClass().equals(JNI.class)); + + expect(getStaticIntField + (JNI.class, fromReflectedField + (JNI.class.getField("field950"))) == 950); + + expect(toReflectedField + (JNI.class, fromReflectedField + (JNI.class.getField("field950")), true) + .getName().equals("field950")); + + { Object o = new Object(); + expect(testLocalRef(o) == o); + } } } diff --git a/test/Longs.java b/test/Longs.java index 649ec6b2ff..4d9395f7b0 100644 --- a/test/Longs.java +++ b/test/Longs.java @@ -385,6 +385,10 @@ public class Longs { { long b = 0xBEL; int x = 0; int y = 0xFF; expect(((b >>> x) & y) == 0xBEL); } + + { long b = 0xFFFFFFFFFFFFFFFFL; int s = 20; + expect((b >>> -s) == 0xFFFFF); + } } } diff --git a/test/Misc.java b/test/Misc.java index 450f23d2b0..91195d5f13 100644 --- a/test/Misc.java +++ b/test/Misc.java @@ -1,4 +1,12 @@ public class Misc { + private static class μClass { + public int μField; + + public void μMethod(int i) { + μField = i; + } + } + private interface Bar { public int baz(); } @@ -237,5 +245,24 @@ public class Misc { System.out.println(new char[] { 'h', 'i' }); expect(! (((Object) new int[0]) instanceof Object[])); + + { μClass μInstance = new μClass(); + μInstance.μMethod(8933); + expect(μInstance.μField == 8933); + } + + expect(new int[0] instanceof Cloneable); + expect(new int[0] instanceof java.io.Serializable); + + expect(new Object[0] instanceof Cloneable); + expect(new Object[0] instanceof java.io.Serializable); + + expect((Baz.class.getModifiers() & java.lang.reflect.Modifier.STATIC) + != 0); + + expect((Protected.class.getModifiers() & java.lang.reflect.Modifier.PUBLIC) + == 0); } + + protected class Protected { } } diff --git a/test/Strings.java b/test/Strings.java index a49b37c9e1..7ba502b9d5 100644 --- a/test/Strings.java +++ b/test/Strings.java @@ -7,6 +7,31 @@ public class Strings { return a == b || (a != null && a.equals(b)); } + private static boolean arraysEqual(byte[] a, byte[] b) { + if (a.length != b.length) { + return false; + } + + for (int i = 0; i < a.length; ++i) { + if (a[i] != b[i]) { + return false; + } + } + + return true; + } + + private static byte[] append(byte[] a, byte[] b) { + byte[] c = new byte[a.length + b.length]; + for (int i = 0; i < a.length; ++i) { + c[i] = a[i]; + } + for (int i = 0; i < b.length; ++i) { + c[i + a.length] = b[i]; + } + return c; + } + private static boolean arraysEqual(Object[] a, Object[] b) { if (a.length != b.length) { return false; @@ -146,5 +171,27 @@ public class Strings { "I", "grape", "nuts", "foobar", new Object() { public String toString() { return "you"; } }) .equals("I enjoy grape nuts. do you? you do?")); + + { java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream(); + java.io.PrintStream pout = new java.io.PrintStream(bout); + String s = "I ♥ grape nuts"; + System.out.println(s); + pout.println(s); + + expect + (arraysEqual + (bout.toByteArray(), + (s + System.getProperty("line.separator")).getBytes())); + + // note that this effectively asserts that the VM's default + // charset is UTF-8. If we want to make this test more + // portable, we should specify the charset explicitly. + expect + (arraysEqual + (bout.toByteArray(), append + (new byte[] { 73, 32, -30, -103, -91, 32, 103, 114, 97, 112, 101, + 32, 110, 117, 116, 115 }, + System.getProperty("line.separator").getBytes()))); + } } } diff --git a/test/Subroutine.java b/test/Subroutine.java index b02ec9ba81..d566f78095 100644 --- a/test/Subroutine.java +++ b/test/Subroutine.java @@ -15,6 +15,23 @@ public class Subroutine { if (! v) throw new RuntimeException(); } + private static void stackMap(Object x) { + while (true) { + try { + try { + System.gc(); + } catch (DummyException e) { + // ignore + } finally { + x.toString(); + } + break; + } catch (DummyException e) { + // ignore + } + } + } + private static byte[] makeTestCode(List pool) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Stream.write2(out, 1); // max stack @@ -292,6 +309,8 @@ public class Subroutine { makeTestClass().getMethod("test", new Class[0]).invoke (null, new Object[0]); + + stackMap(new Object()); } private static class DummyException extends RuntimeException { } diff --git a/test/ci.sh b/test/ci.sh new file mode 100755 index 0000000000..c1863360b2 --- /dev/null +++ b/test/ci.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +make ${flags} test +make ${flags} mode=debug test +make ${flags} process=interpret test +# bootimage and openjdk builds without openjdk-src don't work: +if [ -z "${openjdk}" ]; then + make ${flags} bootimage=true test +fi +make ${flags} tails=true continuations=true test diff --git a/test/jni.cpp b/test/jni.cpp index 8c1ea91f05..8b8d6dc8c9 100644 --- a/test/jni.cpp +++ b/test/jni.cpp @@ -37,6 +37,76 @@ Java_JNI_addMix + a14 + a15 + a16 + a17 + a18 + a19 + a20; } +extern "C" JNIEXPORT jfloat JNICALL +Java_JNI_doEcho__F(JNIEnv* e, jclass c, jfloat f) +{ + jvalue value; + value.f = f; + jvalue array[] = { value }; + return e->CallStaticFloatMethodA + (c, e->GetStaticMethodID(c, "echo", "(F)F"), array); +} + +extern "C" JNIEXPORT jdouble JNICALL +Java_JNI_doEcho__D(JNIEnv* e, jclass c, jdouble f) +{ + jvalue value; + value.d = f; + jvalue array[] = { value }; + return e->CallStaticDoubleMethodA + (c, e->GetStaticMethodID(c, "echo", "(D)D"), array); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_JNI_fromReflectedMethod(JNIEnv* e, jclass, jobject method) +{ + return reinterpret_cast(e->FromReflectedMethod(method)); +} + +extern "C" JNIEXPORT jobject JNICALL +Java_JNI_toReflectedMethod(JNIEnv* e, jclass, jclass c, jlong id, + jboolean isStatic) +{ + return e->ToReflectedMethod(c, reinterpret_cast(id), isStatic); +} + +extern "C" JNIEXPORT jint JNICALL +Java_JNI_callStaticIntMethod(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->CallStaticIntMethod(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jobject JNICALL +Java_JNI_newObject(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->NewObject(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_JNI_fromReflectedField(JNIEnv* e, jclass, jobject field) +{ + return reinterpret_cast(e->FromReflectedField(field)); +} + +extern "C" JNIEXPORT jobject JNICALL +Java_JNI_toReflectedField(JNIEnv* e, jclass, jclass c, jlong id, + jboolean isStatic) +{ + return e->ToReflectedField(c, reinterpret_cast(id), isStatic); +} + +extern "C" JNIEXPORT jint JNICALL +Java_JNI_getStaticIntField(JNIEnv* e, jclass, jclass c, jlong id) +{ + return e->GetStaticIntField(c, reinterpret_cast(id)); +} + +extern "C" JNIEXPORT jobject JNICALL +Java_JNI_testLocalRef(JNIEnv* e, jclass, jobject o) +{ + return e->NewLocalRef(o); +} + extern "C" JNIEXPORT jobject JNICALL Java_Buffers_allocateNative(JNIEnv* e, jclass, jint capacity) { diff --git a/test/test.sh b/test/test.sh index defd6af06a..ddb9f5b1f0 100644 --- a/test/test.sh +++ b/test/test.sh @@ -1,20 +1,33 @@ #!/bin/sh -log=build/log.txt vg="nice valgrind --leak-check=full --num-callers=32 \ --freelist-vol=100000000 --error-exitcode=1" +ld_path=${1}; shift +unit_tester=${1}; shift vm=${1}; shift mode=${1}; shift flags=${1}; shift tests=${@} +log=log.txt + +export ${ld_path} + echo -n "" >${log} +printf "%12s------- Unit tests -------\n" "" +${unit_tester} 2>>${log} +if [ "${?}" != "0" ]; then + trouble=1 + echo "unit tests failed!" +fi + echo +printf "%12s------- Java tests -------\n" "" for test in ${tests}; do - printf "%24s" "${test}: " + printf "%24s: " "${test}" case ${mode} in debug|debug-fast|fast|small ) @@ -41,4 +54,5 @@ echo if [ -n "${trouble}" ]; then printf "see ${log} for output\n" + exit -1 fi diff --git a/unittest/codegen/assembler-test.cpp b/unittest/codegen/assembler-test.cpp new file mode 100644 index 0000000000..4493bcf023 --- /dev/null +++ b/unittest/codegen/assembler-test.cpp @@ -0,0 +1,96 @@ +/* Copyright (c) 2008-2011, 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 + +#include "avian/common.h" +#include +#include +#include "avian/target.h" + +#include +#include +#include +#include + +#include "test-harness.h" + + +using namespace avian::codegen; +using namespace vm; + +class BasicEnv { +public: + System* s; + Heap* heap; + Architecture* arch; + + BasicEnv(): + s(makeSystem(0)), + heap(makeHeap(s, 32 * 1024)), + arch(makeArchitectureNative(s, true)) + { + arch->acquire(); + } + + ~BasicEnv() { + arch->release(); + s->dispose(); + } +}; + +class Asm { +public: + Zone zone; + Assembler* a; + + Asm(BasicEnv& env): + zone(env.s, env.heap, 8192), + a(env.arch->makeAssembler(env.heap, &zone)) + { } + + ~Asm() { + a->dispose(); + } +}; + + +class BasicAssemblerTest : public Test { +public: + BasicAssemblerTest(): + Test("BasicAssembler") + {} + + virtual void run() { + BasicEnv env; + Asm a(env); + } +} basicAssemblerTest; + +class ArchitecturePlanTest : public Test { +public: + ArchitecturePlanTest(): + Test("ArchitecturePlan") + {} + + virtual void run() { + BasicEnv env; + + for(int op = (int)lir::Call; op < (int)lir::AlignedJump; op++) { + bool thunk; + OperandMask mask; + env.arch->plan((lir::UnaryOperation)op, vm::TargetBytesPerWord, mask, &thunk); + assertFalse(thunk); + assertNotEqual(static_cast(0), mask.typeMask); + assertNotEqual(static_cast(0), mask.registerMask); + } + + } +} architecturePlanTest; diff --git a/unittest/codegen/registers-test.cpp b/unittest/codegen/registers-test.cpp new file mode 100644 index 0000000000..946e45ebab --- /dev/null +++ b/unittest/codegen/registers-test.cpp @@ -0,0 +1,44 @@ +/* Copyright (c) 2008-2011, 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 + +#include + +#include "test-harness.h" + + +using namespace avian::codegen; +using namespace vm; + + +class RegisterIteratorTest : public Test { +public: + RegisterIteratorTest(): + Test("RegisterIterator") + {} + + virtual void run() { + RegisterMask regs(0x55); + assertEqual(0, regs.start); + assertEqual(7, regs.limit); + + RegisterIterator it(regs); + assertTrue(it.hasNext()); + assertEqual(0, it.next()); + assertTrue(it.hasNext()); + assertEqual(2, it.next()); + assertTrue(it.hasNext()); + assertEqual(4, it.next()); + assertTrue(it.hasNext()); + assertEqual(6, it.next()); + assertFalse(it.hasNext()); + } +} registerIteratorTest; diff --git a/unittest/test-harness.cpp b/unittest/test-harness.cpp new file mode 100644 index 0000000000..ab51d1aba4 --- /dev/null +++ b/unittest/test-harness.cpp @@ -0,0 +1,52 @@ +/* Copyright (c) 2008-2011, 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 + +#include "test-harness.h" + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) { abort(); } + +Test* Test::first = 0; +Test** Test::last = &first; + +Test::Test(const char* name): + next(0), + failures(0), + runs(0), + name(name) +{ + *last = this; + last = &next; +} + +bool Test::runAll() { + int failures = 0; + for(Test* t = Test::first; t; t = t->next) { + printf("%24s: ", t->name); + t->run(); + failures += t->failures; + if(t->failures > 0) { + printf("failure\n"); + } else { + printf("success\n"); + } + } + return failures == 0; +} + +int main(int argc UNUSED, char** argv UNUSED) { + if(Test::runAll()) { + return 0; + } + return 1; +} \ No newline at end of file diff --git a/unittest/test-harness.h b/unittest/test-harness.h new file mode 100644 index 0000000000..91b551462f --- /dev/null +++ b/unittest/test-harness.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2008-2011, 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. */ + +#ifndef TEST_HARNESS_H +#define TEST_HARNESS_H + +#include "avian/common.h" +#include + +class Test { +private: + Test* next; + static Test* first; + static Test** last; + + friend int main(int argc, char** argv); + + + void print(uint64_t value) { + fprintf(stderr, "%p", reinterpret_cast(value)); + } + + void print(uint32_t value) { + fprintf(stderr, "%p", reinterpret_cast(value)); + } + + + void print(uint8_t value) { + print(static_cast(value)); + } + + void print(bool value) { + fprintf(stderr, "%s", value ? "true" : "false"); + } + + int failures; + int runs; + +protected: + template + void assertEqual(T expected, T actual) { + if(expected != actual) { + fprintf(stderr, "assertion failure, expected: "); + print(expected); + fprintf(stderr, ", actual: "); + print(actual); + fprintf(stderr, "\n"); + failures++; + } + runs++; + } + + void assertEqual(const char* expected, const char* actual) { + if((expected == 0 && actual != 0) || (expected != 0 && actual == 0) || strcmp(expected, actual) != 0) { + fprintf(stderr, "assertion failure, expected: \"%s\", actual: \"%s\"\n", expected, actual); + failures++; + } + runs++; + } + + template + void assertNotEqual(T expected, T actual) { + if(expected == actual) { + fprintf(stderr, "assertion failure, expected: not "); + print(expected); + fprintf(stderr, ", actual: "); + print(actual); + fprintf(stderr, "\n"); + failures++; + } + runs++; + } + + void assertTrue(bool value) { + assertEqual(true, value); + } + + void assertFalse(bool value) { + assertEqual(false, value); + } + +public: + const char* const name; + Test(const char* name); + + virtual void run() = 0; + + static bool runAll(); +}; + +#endif // TEST_HARNESS_H diff --git a/unittest/util/arg-parser-test.cpp b/unittest/util/arg-parser-test.cpp new file mode 100644 index 0000000000..e431f5ec6b --- /dev/null +++ b/unittest/util/arg-parser-test.cpp @@ -0,0 +1,69 @@ +/* Copyright (c) 2008-2011, 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 + +#include "avian/common.h" + +#include + +#include "test-harness.h" + +using namespace avian::util; + +class ArgParserTest : public Test { +public: + ArgParserTest(): + Test("ArgParser") + {} + + virtual void run() { + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] = { + "myExecutable", + "-arg1", "myValue1", + "-required2", "myRequired2", + 0 + }; + assertTrue(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + assertEqual("myValue1", arg1.value); + assertEqual("myRequired2", required2.value); + } + + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] = { + "myExecutable", + "-arg1", "myValue1", + "-required2", + 0 + }; + assertFalse(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + } + + { + ArgParser parser; + Arg arg1(parser, false, "arg1", ""); + Arg required2(parser, true, "required2", ""); + const char* args[] = { + "myExecutable", + "-arg1", "myValue1", + 0 + }; + assertFalse(parser.parse(sizeof(args) / sizeof(char*) - 1, args)); + } + } +} argParserTest; \ No newline at end of file diff --git a/vm.pro b/vm.pro index 4cdb1654ee..19bc238967 100644 --- a/vm.pro +++ b/vm.pro @@ -54,6 +54,7 @@ -keep public class java.lang.ArrayIndexOutOfBoundsException -keep public class java.lang.ArrayStoreException -keep public class java.lang.NegativeArraySizeException +-keep public class java.lang.CloneNotSupportedException -keep public class java.lang.ClassCastException -keep public class java.lang.ClassNotFoundException -keep public class java.lang.NullPointerException @@ -71,6 +72,7 @@ -keep public class java.io.IOException -keep public class java.io.FileNotFoundException -keep public class java.net.SocketException +-keep public class java.util.Locale # ClassLoader.getSystemClassloader() depends on the existence of this class: