This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
[BC ABI] For entertainment purposes only: gcj-JIT
- From: Andrew Haley <aph at redhat dot com>
- To: java-patches at gcc dot gnu dot org
- Date: Thu, 5 Aug 2004 18:36:23 +0100
- Subject: [BC ABI] For entertainment purposes only: gcj-JIT
This works by intercepting the call to ClassLoader.defineClass() to
call gcj on the fly to convert a bytecode file into a precompiled
shared library. These get cached in a directory of your choosing.
While I was doing this I fixed a few runtime bugs.
As the title here says, this isn't yet ready for Prime Time, but IMO
it's a pretty impressive demonstration of the BC ABI.
The runtime, in particular the way class registration gets done, is
kinda wrong. I'm hoping that Tom Tromey can find the time to tidy it
up a bit.
I'll some gcc patches tomorrow.
Andrew.
2004-08-05 Andrew Haley <aph@redhat.com>
* testsuite/libjava.compile/compile.exp: Force
-findirect-dispatch.
* java/security/BasicPermission.java: Remove bogus checks.
* java/net/URLClassLoader.java (JarURLLoader.JarURLLoader): Look
aside for "GCJLIBS" in directory where jarfiles are loaded.
(JarURLLoader.getClass): New method.
(JarURLLoader.toString): New method.
(FileResource.toString): New method.
* java/lang/natSystem.cc (getenv0): New method.
* java/lang/natClassLoader.cc (_Jv_RegisterClassHookDefault):
Remove "Duplicate class registration: " bug.
(_registerClass): New method.
* java/lang/natClass.cc (_Jv_LinkSymbolTable): Check method index.
(_Jv_LinkSymbolTable): Call _Jv_LayoutClass().
Add debugging.
(_Jv_LayoutClass): Use getSuperclass() rather than directly
accessing the field.
* java/lang/ClassLoader.java (SharedLibHelpers): New variable.
(defineClass): Call gcj to JIT-compile a class.
(_registerClass): New method.
* gnu/gcj/runtime/SharedLibHelper.java (findHelper): A shared
library name can refer to more than one loaded library, so use a
Set of SharedLibHelpers.
If a shared library is already loaded, take a copy.
(copyFile): New function.
Index: gnu/gcj/runtime/SharedLibHelper.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/SharedLibHelper.java,v
retrieving revision 1.1.32.2
diff -p -2 -c -r1.1.32.2 SharedLibHelper.java
*** gnu/gcj/runtime/SharedLibHelper.java 20 May 2004 23:33:45 -0000 1.1.32.2
--- gnu/gcj/runtime/SharedLibHelper.java 5 Aug 2004 17:33:41 -0000
*************** import java.util.HashMap;
*** 14,17 ****
--- 14,22 ----
import java.security.*;
import gnu.gcj.Core;
+ import java.util.Set;
+ import java.util.Iterator;
+ import java.util.HashSet;
+ import java.nio.channels.FileChannel;
+ import java.io.*;
public class SharedLibHelper
*************** public class SharedLibHelper
*** 40,50 ****
synchronized (map)
{
! WeakReference ref = (WeakReference) map.get(libname);
! if (ref != null)
! return (SharedLibHelper) ref.get();
return null;
}
}
public static SharedLibHelper findHelper (ClassLoader loader, String libname,
CodeSource source)
--- 45,70 ----
synchronized (map)
{
! Set s = (Set)map.get(libname);
! if (s == null)
! return null;
! for (Iterator i=s.iterator(); i.hasNext();)
! {
! WeakReference ref = (WeakReference)i.next();
! if (ref != null)
! return (SharedLibHelper) ref.get();
! }
return null;
}
}
+ static void copyFile (File in, File out) throws java.io.IOException
+ {
+ FileChannel source = new FileInputStream(in).getChannel();
+ FileChannel destination = new FileOutputStream(out).getChannel();
+ source.transferTo(0, source.size(), destination);
+ source.close();
+ destination.close();
+ }
+
public static SharedLibHelper findHelper (ClassLoader loader, String libname,
CodeSource source)
*************** public class SharedLibHelper
*** 53,71 ****
{
SharedLibHelper result;
! WeakReference ref = (WeakReference) map.get(libname);
! if (ref != null)
{
! result = (SharedLibHelper) ref.get();
! if (result != null)
{
! if (result.loader != loader)
! // FIXME
! throw new UnknownError();
! return result;
}
- }
result = new SharedLibHelper(libname, loader, source, 0);
! map.put(libname, new WeakReference(result));
return result;
}
--- 73,124 ----
{
SharedLibHelper result;
! Set s = (Set)map.get(libname);
! if (s == null)
{
! s = new HashSet();
! map.put(libname, s);
! }
! else
! {
! for (Iterator i=s.iterator(); i.hasNext();)
{
! WeakReference ref = (WeakReference)i.next();
! if (ref != null)
! {
! result = (SharedLibHelper) ref.get();
! if (result != null)
! {
! // A match succeeds if the library is already
! // loaded by LOADER or any of its ancestors.
! ClassLoader l = loader;
! do
! {
! if (result.loader == l)
! return result;
! l = l.getParent();
! }
! while (l != null);
! }
! }
}
+ // Oh dear. We've already mapped this shared library, but
+ // with a different class loader. We need to copy it.
+ try
+ {
+ File copy
+ = File.createTempFile(new File(libname).getName(),
+ ".so", new File ("/tmp"));
+ File src = new File(libname);
+ copyFile (src, copy);
+ libname = copy.getPath();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
result = new SharedLibHelper(libname, loader, source, 0);
! s.add(new WeakReference(result));
return result;
}
*************** public class SharedLibHelper
*** 77,92 ****
{
ensureInit();
! Class c = (Class) classMap.get(name);
if (c != null)
! {
! String s = System.getProperty("gnu.classpath.verbose");
! if (s != null && s.equals("class"))
! if (h.get(name) == null)
! {
! System.err.println("[Loading class " + name
! + " from " + this + "]");
! h.put(name,name);
! }
! }
return c;
}
--- 130,145 ----
{
ensureInit();
! Class c = (Class)classMap.get(name);
if (c != null)
! {
! String s = System.getProperty("gnu.classpath.verbose");
! if (s != null && s.equals("class"))
! if (h.get(name) == null)
! {
! System.err.println("[Loading class " + name
! + " from " + this + "]");
! h.put(name,name);
! }
! }
return c;
}
Index: java/lang/ClassLoader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/ClassLoader.java,v
retrieving revision 1.31.18.2
diff -p -2 -c -r1.31.18.2 ClassLoader.java
*** java/lang/ClassLoader.java 20 May 2004 23:34:03 -0000 1.31.18.2
--- java/lang/ClassLoader.java 5 Aug 2004 17:33:43 -0000
*************** import java.security.ProtectionDomain;
*** 53,58 ****
--- 53,60 ----
import java.util.Enumeration;
import java.util.HashMap;
+ import java.util.IdentityHashMap;
import java.util.Map;
+
/**
* The ClassLoader is a way of customizing the way Java gets its classes
*************** public abstract class ClassLoader
*** 212,215 ****
--- 214,220 ----
Map classAssertionStatus;
+ // All our SharedLibHelpers: when we get gc'd, so do they.
+ Map SharedLibHelpers = new IdentityHashMap();
+
/**
* Create a new ClassLoader with as parent the system classloader. There
*************** public abstract class ClassLoader
*** 451,495 ****
if (! initialized)
throw new SecurityException("attempt to define class from uninitialized class loader");
! Class retval = VMClassLoader.defineClass(this, name, data,
! offset, len, domain);
!
! // FIXME: This is a temporary hack that allows you to compile some
! // class files into a .so called "gcjlib.so" in the same
! // directory. To be removed as soon as gcj-JIT is ready.
! java.security.CodeSource source = domain.getCodeSource();
! java.net.URL url = source != null ? source.getLocation() : null;
! String filename = url.getFile();
! if (filename != null)
{
! File f = new File(filename);
! if (f.isDirectory())
{
! try
{
Class c = null;
! String libname = filename + "gcjlib.so";
! File soFile = new File (libname);
! if (soFile.isFile())
{
! SharedLibHelper helper
! = SharedLibHelper.findHelper (this, libname, source);
! c = helper.findClass (retval.getName());
}
- if (c != null)
- retval = c;
}
! catch (UnknownError _)
{
}
}
}
{
String s = System.getProperty("gnu.classpath.verbose");
if (s != null && s.equals("class"))
{
! // java.security.CodeSource source = domain.getCodeSource();
! // java.net.URL url = source != null ? source.getLocation() : null;
String URLname = url != null ? url.toString() : null;
if (URLname == null)
--- 456,561 ----
if (! initialized)
throw new SecurityException("attempt to define class from uninitialized class loader");
+
+ Class retval = null;
! String gcjJitCompiler = System.getenv0 ("GCJ_JIT_COMPILER");
! if (gcjJitCompiler != null)
{
! compile:
! try
{
! // FIXME: Make sure that the class represented by the
! // bytes in DATA really is the class named in NAME. Make
! // sure it's not "java.*".
! java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
! byte digest[] = md.digest(data);
! String tmpdir = System.getenv0("GCJ_JIT_TMPDIR");
! if (tmpdir == null)
! tmpdir = "/tmp";
! StringBuffer hexBytes = new StringBuffer(tmpdir);
! hexBytes.append("/");
! int digestLength = digest.length;
! for (int i=0; i<digestLength; i++)
! hexBytes.append(Integer.toHexString(digest[i]&0xff));
! StringBuffer fileName = new StringBuffer(hexBytes.toString());
! fileName.append(".so");
! File soFile = new File (fileName.toString());
! if (soFile.isFile())
{
Class c = null;
! SharedLibHelper helper
! = SharedLibHelper.findHelper (this, fileName.toString(),
! domain.getCodeSource());
! c = helper.findClass (name);
! if (c != null)
{
! SharedLibHelpers.put(helper,c);
! retval = c;
! _registerClass(retval);
! break compile;
}
}
! File classFile = new File(hexBytes+".class");
! classFile.delete();
! if (classFile.createNewFile() != true)
! throw new IOException ();
! java.io.FileOutputStream f = new java.io.FileOutputStream (classFile);
! byte[] b = new byte[len];
! System.arraycopy(data,offset,b,0,len);
! f.write(b);
!
! StringBuffer command = new StringBuffer(gcjJitCompiler);
! command.append(" ");
! command.append(classFile);
! command.append(" ");
! String opts = System.getenv0("GCJ_JIT_OPTIONS");
! if (opts != null)
! command.append(opts);
! command.append(" -g -findirect-dispatch -shared -fPIC -o ");
! command.append(fileName);
! Process p = Runtime.getRuntime().exec(command.toString());
! StringBuffer err = new StringBuffer();
! {
! InputStream stderr = p.getErrorStream();
! int ch;
! // FIXME: We need a much better way to handle error output
! // from the compiler.
! while ((ch = stderr.read()) != -1)
! err.append((char)ch);
! }
! if (p.waitFor() != 0)
{
+ System.err.println(err.toString());
+ throw new IOException ();
}
+ {
+ Class c = null;
+ SharedLibHelper helper
+ = SharedLibHelper.findHelper (this, fileName.toString(),
+ domain.getCodeSource());
+ c = helper.findClass (name);
+ if (c != null)
+ {
+ SharedLibHelpers.put(helper,c);
+ retval = c;
+ _registerClass(retval);
+ }
+ }
+ }
+ catch (Exception _)
+ {
}
}
+ if (retval == null)
+ retval = VMClassLoader.defineClass(this, name, data,
+ offset, len, domain);
+
{
String s = System.getProperty("gnu.classpath.verbose");
if (s != null && s.equals("class"))
{
! java.security.CodeSource source = domain.getCodeSource();
! java.net.URL url = source != null ? source.getLocation() : null;
String URLname = url != null ? url.toString() : null;
if (URLname == null)
*************** public abstract class ClassLoader
*** 1016,1018 ****
--- 1082,1086 ----
static private final native ClassLoader getClassLoader0(Class c);
+
+ private final native void _registerClass(Class c);
}
Index: java/lang/System.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/System.java,v
retrieving revision 1.14.18.1
diff -p -2 -c -r1.14.18.1 System.java
*** java/lang/System.java 20 May 2004 23:34:05 -0000 1.14.18.1
--- java/lang/System.java 5 Aug 2004 17:33:43 -0000
*************** public final class System
*** 602,604 ****
--- 602,611 ----
*/
private static native void setErr0(PrintStream err);
+
+ /**
+ * Gets the value of an environment variable.
+ *
+ * @see #getenv(String)
+ */
+ static native String getenv0(String name);
} // class System
Index: java/lang/natClass.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natClass.cc,v
retrieving revision 1.75.2.5
diff -p -2 -c -r1.75.2.5 natClass.cc
*** java/lang/natClass.cc 24 May 2004 15:36:37 -0000 1.75.2.5
--- java/lang/natClass.cc 5 Aug 2004 17:33:44 -0000
*************** _Jv_LinkSymbolTable(jclass klass)
*** 1703,1708 ****
if (meth != NULL)
{
! klass->otable->offsets[index] =
! _Jv_VTable::idx_to_offset (meth->index);
}
if (debug_link)
--- 1703,1711 ----
if (meth != NULL)
{
! int offset = _Jv_VTable::idx_to_offset (meth->index);
! if (offset == -1)
! JvFail ("Bad method index");
! JvAssert (meth->index < target_class->vtable_method_count);
! klass->otable->offsets[index] = offset;
}
if (debug_link)
*************** _Jv_LinkSymbolTable(jclass klass)
*** 1733,1736 ****
--- 1736,1747 ----
// {
+ // FIXME size_in_bytes == -1 is an evil way to test
+ // for BC compiled programs
+ if (cls->size_in_bytes == (jint)-1)
+ {
+ int static_size;
+ _Jv_LayoutClass(cls, &static_size);
+ }
+
if (!field->isResolved ())
_Jv_ResolveField (field, cls->loader);
*************** _Jv_LinkSymbolTable(jclass klass)
*** 1742,1745 ****
--- 1753,1763 ----
the_field = field;
+ if (debug_link)
+ fprintf (stderr, " offsets[%d] = %d (class %s@%p : %s)\n",
+ index,
+ field->u.boffset,
+ (const char*)cls->name->data,
+ cls,
+ (const char*)field->name->data);
goto end_of_field_search;
}
*************** _Jv_LayoutClass(jclass klass, int *stati
*** 2111,2115 ****
// could consider caching this in the Class.
int max_align = __alignof__ (java::lang::Object);
! jclass super = klass->superclass;
while (super != NULL)
{
--- 2129,2133 ----
// could consider caching this in the Class.
int max_align = __alignof__ (java::lang::Object);
! jclass super = klass->getSuperclass();
while (super != NULL)
{
*************** _Jv_LayoutClass(jclass klass, int *stati
*** 2127,2131 ****
--num;
}
! super = super->superclass;
}
--- 2145,2149 ----
--num;
}
! super = super->getSuperclass();
}
Index: java/lang/natClassLoader.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natClassLoader.cc,v
retrieving revision 1.64.8.2
diff -p -2 -c -r1.64.8.2 natClassLoader.cc
*** java/lang/natClassLoader.cc 20 Apr 2004 21:59:40 -0000 1.64.8.2
--- java/lang/natClassLoader.cc 5 Aug 2004 17:33:44 -0000
*************** _Jv_RegisterClassHookDefault (jclass kla
*** 343,346 ****
--- 343,350 ----
jclass check_class = loaded_classes[hash];
+ // The BC ABI makes this check unnecessary: we always resolve all
+ // data references via the appropriate class loader, so the kludge
+ // that required this check has gone.
+ #if 0
// If the class is already registered, don't re-register it.
while (check_class != NULL)
*************** _Jv_RegisterClassHookDefault (jclass kla
*** 368,371 ****
--- 372,376 ----
check_class = check_class->next;
}
+ #endif
klass->next = loaded_classes[hash];
*************** _Jv_NewArrayClass (jclass element, java:
*** 585,588 ****
--- 590,599 ----
}
+ void
+ ::java::lang::ClassLoader::_registerClass (::java::lang::Class *c)
+ {
+ _Jv_RegisterClass (c);
+ }
+
static jclass stack_head;
Index: java/lang/natSystem.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natSystem.cc,v
retrieving revision 1.56
diff -p -2 -c -r1.56 natSystem.cc
*** java/lang/natSystem.cc 23 Jul 2003 15:31:43 -0000 1.56
--- java/lang/natSystem.cc 5 Aug 2004 17:33:44 -0000
*************** java::lang::System::identityHashCode (jo
*** 131,134 ****
--- 131,147 ----
}
+ jstring
+ java::lang::System::getenv0 (jstring name)
+ {
+ jint len = _Jv_GetStringUTFLength (name);
+ char buf[len + 1];
+ jsize total = JvGetStringUTFRegion (name, 0, name->length(), buf);
+ buf[total] = '\0';
+ const char *value = ::getenv (buf);
+ if (value == NULL)
+ return NULL;
+ return JvNewStringUTF (value);
+ }
+
jboolean
java::lang::System::isWordsBigEndian (void)
Index: java/net/URLClassLoader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/URLClassLoader.java,v
retrieving revision 1.16.18.1
diff -p -2 -c -r1.16.18.1 URLClassLoader.java
*** java/net/URLClassLoader.java 20 May 2004 23:34:08 -0000 1.16.18.1
--- java/net/URLClassLoader.java 5 Aug 2004 17:33:44 -0000
*************** public class URLClassLoader extends Secu
*** 290,293 ****
--- 290,295 ----
final URL baseJarURL; // Base jar: url for all resources loaded from jar
+ SoURLLoader soURLLoader;
+
public JarURLLoader(URLClassLoader classloader, URL baseURL)
{
*************** public class URLClassLoader extends Secu
*** 302,315 ****
String jarURL = sb.toString();
URL baseJarURL = null;
JarFile jarfile = null;
try
! {
! baseJarURL =
! new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
! jarfile =
! ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
! }
catch (IOException ioe)
{
--- 304,339 ----
String jarURL = sb.toString();
+ this.soURLLoader = null;
URL baseJarURL = null;
JarFile jarfile = null;
try
! {
! baseJarURL
! = new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
! jarfile
! = ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
! if (jarfile != null)
! {
! String fileName = baseURL.getFile();
! if (fileName != null)
! {
! File f = new File(fileName);
! String libDirName = f.getCanonicalFile().getParent()
! + File.separator + "GCJLIBS";
! File libDir = new File(libDirName);
! if (libDir != null && (libDir.isDirectory()))
! {
! File soFile = new File (libDirName + File.separator + f.getName()
! + ".so");
! if (soFile != null && soFile.isFile())
! this.soURLLoader =
! new SoURLLoader
! (classloader,
! new URL ("file", null, soFile.getCanonicalPath()));
! }
! }
! }
! }
catch (IOException ioe)
{
*************** public class URLClassLoader extends Secu
*** 321,324 ****
--- 345,355 ----
}
+ Class getClass(String className)
+ {
+ if (soURLLoader != null)
+ return soURLLoader.getClass(className);
+ return null;
+ }
+
/** get resource with the name "name" in the jar url */
Resource getResource(String name)
*************** public class URLClassLoader extends Secu
*** 337,340 ****
--- 368,376 ----
}
+ public String toString ()
+ {
+ return "jarfile " + jarfile.getName();
+ }
+
Manifest getManifest()
{
*************** public class URLClassLoader extends Secu
*** 577,580 ****
--- 613,621 ----
}
+ public String toString ()
+ {
+ return "file " +file.getAbsolutePath();
+ }
+
public URL getURL()
{
Index: java/security/BasicPermission.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/security/BasicPermission.java,v
retrieving revision 1.6
diff -p -2 -c -r1.6 BasicPermission.java
*** java/security/BasicPermission.java 14 Jun 2003 05:45:12 -0000 1.6
--- java/security/BasicPermission.java 5 Aug 2004 17:33:45 -0000
*************** public abstract class BasicPermission ex
*** 81,87 ****
/**
! * Create a new instance with the specified permission name. If the name
! * is empty, or contains an illegal wildcard character, an exception is
! * thrown.
*
* @param name the name of this permission
--- 81,86 ----
/**
! * Create a new instance with the specified permission name. If the
! * name is empty an exception is thrown.
*
* @param name the name of this permission
*************** public abstract class BasicPermission ex
*** 92,101 ****
{
super(name);
! if (name.indexOf("*") != -1)
! {
! if ((! name.endsWith(".*") && ! name.equals("*"))
! || name.indexOf("*") != name.lastIndexOf("*"))
! throw new IllegalArgumentException("Bad wildcard: " + name);
! }
if ("".equals(name))
throw new IllegalArgumentException("Empty name");
--- 91,105 ----
{
super(name);
!
! // FIXME: this arg checking isn't in the spec, and Sun's JDK
! // doesn't do it.
!
! // if (name.indexOf("*") != -1)
! // {
! // if ((! name.endsWith(".*") && ! name.equals("*"))
! // || name.indexOf("*") != name.lastIndexOf("*"))
! // throw new IllegalArgumentException("Bad wildcard: " + name);
! // }
!
if ("".equals(name))
throw new IllegalArgumentException("Empty name");
Index: testsuite/libjava.compile/compile.exp
===================================================================
RCS file: /cvs/gcc/gcc/libjava/testsuite/libjava.compile/compile.exp,v
retrieving revision 1.4
diff -p -2 -c -r1.4 compile.exp
*** testsuite/libjava.compile/compile.exp 5 Sep 2003 01:53:46 -0000 1.4
--- testsuite/libjava.compile/compile.exp 5 Aug 2004 17:33:45 -0000
*************** foreach x $srcfiles {
*** 12,17 ****
lappend args no-exec
! test_libjava "" "$x" "" "" "" $args
! test_libjava "" "$x" "-O3" "" "" $args
}
--- 12,17 ----
lappend args no-exec
! test_libjava "" "$x" "-findirect-dispatch " "" "" $args
! test_libjava "" "$x" "-findirect-dispatch -O3" "" "" $args
}