Skip to content

Commit

Permalink
Lift constant handle caching to all objects and wire it up.
Browse files Browse the repository at this point in the history
Added handle reuse for:

* true/false
* nil
* runtime

And fixed handling for fixnums and symbols.

See #2058.
  • Loading branch information
headius committed Oct 20, 2014
1 parent 76ca38b commit efabe06
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 69 deletions.
19 changes: 18 additions & 1 deletion core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
***** END LICENSE BLOCK *****/
package org.jruby;

import org.jruby.compiler.Constantizable;
import org.objectweb.asm.util.TraceClassVisitor;
import jnr.constants.Constant;
import jnr.constants.ConstantSet;
Expand Down Expand Up @@ -194,7 +195,7 @@
* provides a number of utility methods for constructing global types and
* accessing global runtime structures.
*/
public final class Ruby {
public final class Ruby implements Constantizable {

/**
* The logger used to log relevant bits.
Expand All @@ -220,6 +221,8 @@ private Ruby(RubyInstanceConfig config) {
this.profiledMethods = null;
this.profilingServiceLookup = null;
}

constant = OptoFactory.newConstantWrapper(Ruby.class, this);

getJRubyClassLoader(); // force JRubyClassLoader to init if possible

Expand Down Expand Up @@ -4630,6 +4633,14 @@ private void setNetworkStack() {
}
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
return constant;
}

@Deprecated
public int getSafeLevel() {
return 0;
Expand Down Expand Up @@ -4982,4 +4993,10 @@ public void addToObjectSpace(boolean useObjectSpace, IRubyObject object) {
private final org.jruby.management.Runtime runtimeBean;

private final FilenoUtil filenoUtil = new FilenoUtil();

/**
* A representation of this runtime as a JIT-optimizable constant. Used for e.g. invokedynamic binding of runtime
* accesses.
*/
private final Object constant;
}
49 changes: 1 addition & 48 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -65,9 +64,9 @@
import org.jruby.runtime.builtin.Variable;
import org.jruby.runtime.component.VariableEntry;
import org.jruby.runtime.marshal.CoreObjectType;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;
import org.jruby.util.io.EncodingUtils;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
Expand Down Expand Up @@ -3052,50 +3051,4 @@ public final Object getNativeHandle() {
@Deprecated
public final void setNativeHandle(Object value) {
}

/**
* A factory for abstract "constant" representations of objects. This is currently only used by our invokedynamic
* support to cache the "constant" handles that wrap common literal fixnums and symbols. See #2058.
*/
static interface ConstantFactory {
public Object create(Class type, Object object);
}

/**
* A constant factory that produces MethodHandle constants that drop an initial ThreadContext argument.
*/
private static class MethodHandleConstantFactory implements ConstantFactory {
public Object create(Class type, Object object) {
return MethodHandles.dropArguments(
MethodHandles.constant(type, object),
0,
ThreadContext.class);
}
}

/**
* A dummy factory, for when we are not running with invokedynamic.
*/
private static class DummyConstantFactory implements ConstantFactory {
public Object create(Class type, Object object) {
return object;
}
}

/**
* The constant factory we'll be using for this run.
*/
private static final ConstantFactory CONSTANT_FACTORY = Options.COMPILE_INVOKEDYNAMIC.load() ?
new MethodHandleConstantFactory() :
new DummyConstantFactory();

/**
* Create a new "constant" representation for this object, conforming to the given concrete type. This is currently
* only used by invokedynamic to cache "constant" method handle wrappers for common literal fixnums and symbols.
* @param type the class to which the constant should conform
* @return a "constant" representation of this object appropriate to the current JVM and runtime modes
*/
protected final Object createConstant(Class type) {
return CONSTANT_FACTORY.create(type, this);
}
}
15 changes: 14 additions & 1 deletion core/src/main/java/org/jruby/RubyBoolean.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,23 @@

import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.compiler.Constantizable;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.marshal.MarshalStream;
import org.jruby.runtime.opto.OptoFactory;

/**
*
* @author jpetersen
*/
@JRubyClass(name={"TrueClass", "FalseClass"})
public class RubyBoolean extends RubyObject {
public class RubyBoolean extends RubyObject implements Constantizable {

private final int hashCode;
private final Object constant;

RubyBoolean(Ruby runtime, boolean value) {
super(runtime,
Expand All @@ -63,6 +66,8 @@ public class RubyBoolean extends RubyObject {
// save the object id based hash code;
this.hashCode = System.identityHashCode(this);
}

constant = OptoFactory.newConstantWrapper(IRubyObject.class, this);
}

@Override
Expand All @@ -85,6 +90,14 @@ public Class<?> getJavaClass() {
return boolean.class;
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
return constant;
}

public static RubyClass createFalseClass(Ruby runtime) {
RubyClass falseClass = runtime.defineClass("FalseClass", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
runtime.setFalseClass(falseClass);
Expand Down
30 changes: 21 additions & 9 deletions core/src/main/java/org/jruby/RubyEncoding.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,21 @@
import org.jcodings.util.Hash.HashEntryIterator;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.compiler.Constantizable;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.encoding.EncodingCapable;
import org.jruby.runtime.encoding.EncodingService;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;
import org.jruby.util.io.EncodingUtils;
import org.jruby.util.unsafe.UnsafeHolder;

@JRubyClass(name="Encoding")
public class RubyEncoding extends RubyObject {
public class RubyEncoding extends RubyObject implements Constantizable {
public static final Charset UTF8 = Charset.forName("UTF-8");
public static final Charset UTF16 = Charset.forName("UTF-16");
public static final Charset ISO = Charset.forName("ISO-8859-1");
Expand All @@ -76,29 +78,39 @@ public static RubyClass createEncodingClass(Ruby runtime) {
private Encoding encoding;
private final ByteList name;
private final boolean isDummy;
private final Object constant;

private RubyEncoding(Ruby runtime, byte[] name, int p, int end, boolean isDummy) {
super(runtime, runtime.getEncoding());
this.name = new ByteList(name, p, end);
this.isDummy = isDummy;
this(runtime, new ByteList(name, p, end), null, false);
}

private RubyEncoding(Ruby runtime, byte[] name, boolean isDummy) {
this(runtime, name, 0, name.length, isDummy);
}

private RubyEncoding(Ruby runtime, Encoding encoding) {
super(runtime, runtime.getEncoding());
this.name = new ByteList(encoding.getName());
this.isDummy = false;
this.encoding = encoding;
this(runtime, new ByteList(encoding.getName()), encoding, false);
}

private RubyEncoding(Ruby runtime, byte[] name, Encoding encoding, boolean isDummy) {
this(runtime, new ByteList(name), encoding, isDummy);
}

private RubyEncoding(Ruby runtime, ByteList name, Encoding encoding, boolean isDummy) {
super(runtime, runtime.getEncoding());
this.name = new ByteList(name);
this.name = name;
this.isDummy = isDummy;
this.encoding = encoding;

this.constant = OptoFactory.newConstantWrapper(RubyEncoding.class, this);
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
return constant;
}

public static RubyEncoding newEncoding(Ruby runtime, byte[] name, int p, int end, boolean isDummy) {
Expand Down
10 changes: 8 additions & 2 deletions core/src/main/java/org/jruby/RubyFixnum.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.compiler.Constantizable;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
Expand All @@ -48,6 +49,7 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
import org.jruby.util.ConvertBytes;
import org.jruby.util.Numeric;
Expand All @@ -61,7 +63,7 @@
* Implementation of the Fixnum class.
*/
@JRubyClass(name="Fixnum", parent="Integer", include="Precision")
public class RubyFixnum extends RubyInteger {
public class RubyFixnum extends RubyInteger implements Constantizable {

public static RubyClass createFixnumClass(Ruby runtime) {
RubyClass fixnum = runtime.defineClass("Fixnum", runtime.getInteger(),
Expand Down Expand Up @@ -126,6 +128,10 @@ public ClassIndex getNativeClassIndex() {
return ClassIndex.FIXNUM;
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
Object constant = null;
long value = this.value;
Expand All @@ -135,7 +141,7 @@ public Object constant() {
constant = fixnumConstants[(int) value];

if (constant == null) {
constant = createConstant(RubyFixnum.class);
constant = OptoFactory.newConstantWrapper(IRubyObject.class, this);
fixnumConstants[(int) value] = constant;
}
}
Expand Down
15 changes: 14 additions & 1 deletion core/src/main/java/org/jruby/RubyNil.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,22 @@

import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.compiler.Constantizable;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.opto.OptoFactory;

/**
*
* @author jpetersen
*/
@JRubyClass(name="NilClass")
public class RubyNil extends RubyObject {
public class RubyNil extends RubyObject implements Constantizable {

private final int hashCode;
private final Object constant;

public RubyNil(Ruby runtime) {
super(runtime, runtime.getNilClass(), false);
Expand All @@ -59,6 +62,8 @@ public RubyNil(Ruby runtime) {
// save the object id based hash code;
this.hashCode = System.identityHashCode(this);
}

constant = OptoFactory.newConstantWrapper(IRubyObject.class, this);
}

public static final ObjectAllocator NIL_ALLOCATOR = new ObjectAllocator() {
Expand Down Expand Up @@ -103,6 +108,14 @@ public RubyClass getSingletonClass() {
public Class<?> getJavaClass() {
return void.class;
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
return constant;
}

// Methods of the Nil Class (nil_*):

Expand Down
10 changes: 8 additions & 2 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.ast.util.ArgsUtil;
import org.jruby.compiler.Constantizable;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Binding;
Expand All @@ -56,6 +57,7 @@
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.encoding.MarshalEncoding;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
import org.jruby.util.PerlHash;
import org.jruby.util.SipHashInline;
Expand All @@ -70,7 +72,7 @@
* Represents a Ruby symbol (e.g. :bar)
*/
@JRubyClass(name="Symbol")
public class RubySymbol extends RubyObject implements MarshalEncoding {
public class RubySymbol extends RubyObject implements MarshalEncoding, Constantizable {
public static final long symbolHashSeedK0 = 5238926673095087190l;

private final String symbol;
Expand Down Expand Up @@ -183,9 +185,13 @@ public static RubySymbol newSymbol(Ruby runtime, String name) {
return runtime.getSymbolTable().getSymbol(name);
}

/**
* @see org.jruby.compiler.Constantizable
*/
@Override
public Object constant() {
return constant == null ?
constant = createConstant(RubySymbol.class) :
constant = OptoFactory.newConstantWrapper(IRubyObject.class, this) :
constant;
}

Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/org/jruby/compiler/Constantizable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jruby.compiler;

/**
* Represents an object that can produce a JIT-optimizable "constant" version of itself. Currently this is only used
* by invokedynamic support to avoid creating duplicate MethodHandles.constant handles for the same value.
*/
public interface Constantizable {
public Object constant();
}
Loading

0 comments on commit efabe06

Please sign in to comment.