Skip to content

Commit

Permalink
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
@@ -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;
@@ -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.
@@ -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

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

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

@Deprecated
public int getSafeLevel() {
return 0;
@@ -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
@@ -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;
@@ -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;
@@ -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
@@ -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,
@@ -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
@@ -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);
30 changes: 21 additions & 9 deletions core/src/main/java/org/jruby/RubyEncoding.java
Original file line number Diff line number Diff line change
@@ -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");
@@ -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) {
10 changes: 8 additions & 2 deletions core/src/main/java/org/jruby/RubyFixnum.java
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
@@ -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(),
@@ -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;
@@ -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;
}
}
15 changes: 14 additions & 1 deletion core/src/main/java/org/jruby/RubyNil.java
Original file line number Diff line number Diff line change
@@ -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);
@@ -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() {
@@ -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_*):

10 changes: 8 additions & 2 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -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;
@@ -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;
@@ -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;
@@ -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;
}

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();
}
32 changes: 27 additions & 5 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.jruby.runtime.invokedynamic.MathLinker;
import org.jruby.runtime.invokedynamic.VariableSite;
import org.jruby.runtime.ivars.VariableAccessor;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.RegexpOptions;
@@ -388,28 +389,49 @@ public static CallSite contextValueString(Lookup lookup, String name, MethodType
}

public static IRubyObject nil(ThreadContext context, MutableCallSite site) {
site.setTarget(dropArguments(constant(IRubyObject.class, context.nil), 0, ThreadContext.class));
MethodHandle constant = (MethodHandle)((RubyNil)context.nil).constant();
if (constant == null) constant = (MethodHandle)OptoFactory.newConstantWrapper(IRubyObject.class, context.nil);

site.setTarget(constant);

return context.nil;
}

public static IRubyObject True(ThreadContext context, MutableCallSite site) {
site.setTarget(dropArguments(constant(IRubyObject.class, context.runtime.getTrue()), 0, ThreadContext.class));
MethodHandle constant = (MethodHandle)context.runtime.getTrue().constant();
if (constant == null) constant = (MethodHandle)OptoFactory.newConstantWrapper(IRubyObject.class, context.runtime.getTrue());

site.setTarget(constant);

return context.runtime.getTrue();
}

public static IRubyObject False(ThreadContext context, MutableCallSite site) {
site.setTarget(dropArguments(constant(IRubyObject.class, context.runtime.getFalse()), 0, ThreadContext.class));
MethodHandle constant = (MethodHandle)context.runtime.getFalse().constant();
if (constant == null) constant = (MethodHandle)OptoFactory.newConstantWrapper(IRubyObject.class, context.runtime.getFalse());

site.setTarget(constant);

return context.runtime.getFalse();
}

public static Ruby runtime(ThreadContext context, MutableCallSite site) {
site.setTarget(dropArguments(constant(Ruby.class, context.runtime), 0, ThreadContext.class));
MethodHandle constant = (MethodHandle)context.runtime.constant();
if (constant == null) constant = (MethodHandle)OptoFactory.newConstantWrapper(Ruby.class, context.runtime);

site.setTarget(constant);

return context.runtime;
}

public static RubyEncoding encoding(ThreadContext context, MutableCallSite site, String name) {
RubyEncoding rubyEncoding = IRRuntimeHelpers.retrieveEncoding(context, name);
site.setTarget(dropArguments(constant(RubyEncoding.class, rubyEncoding), 0, ThreadContext.class));

MethodHandle constant = (MethodHandle)rubyEncoding.constant();
if (constant == null) constant = (MethodHandle)OptoFactory.newConstantWrapper(RubyEncoding.class, rubyEncoding);

site.setTarget(constant);

return rubyEncoding;
}

58 changes: 58 additions & 0 deletions core/src/main/java/org/jruby/runtime/opto/OptoFactory.java
Original file line number Diff line number Diff line change
@@ -27,13 +27,27 @@
package org.jruby.runtime.opto;

import org.jruby.RubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.util.cli.Options;

import java.lang.invoke.MethodHandles;

/**
* A set of factory methods to construct optimizing utilities for compilation,
* cache invalidation, and so on.
*/
public class OptoFactory {

/**
* 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
*/
public static final Object newConstantWrapper(Class type, Object object) {
return OptoFactory.CONSTANT_FACTORY.create(type, object);
}

public static Invalidator newConstantInvalidator() {
if (indyEnabled() && indyConstants()) {
try {
@@ -91,4 +105,48 @@ private static Boolean indyInvocationSwitchpoint() {
private static void disableIndy() {
Options.COMPILE_INVOKEDYNAMIC.force("false");
}

/**
* 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.
*/
public static interface ConstantFactory {
/**
* Return a representation of a "constant" suitable for optimization in the current runtime. For invokedynamic,
* this produces a MethodHandles.constant wrapper around the given object, typed with the given type.
*
* @param type the type to which the constant should conform
* @param object the object which represents the constant's value
* @return a constant representation suitable for optimization
*/
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 null;
}
}

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

0 comments on commit efabe06

Please sign in to comment.