Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6206683241b9
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 96278faf37bd
Choose a head ref
  • 14 commits
  • 34 files changed
  • 4 contributors

Commits on Dec 28, 2015

  1. Various literal to_proc'ed Symbol optimizations.

    * Use a dummy binding rather than creating new every time.
    * Cache the resulting proc at create site.
    
    The second optimization results in a given &:foo in code only
    creating a single Proc, ever, and caching it at that point in the
    code. This is based on the observation that symbol procs typically
    are used to iterate over homogeneous collections of objects, so
    caching the proc allows its cache to stay populated and local to
    the related code. This also eliminates the allocation of a Block,
    BlockBody, and RubyProc for each encounter, which improves perf
    also for heterogeneous collections with poor cacheability.
    
    Benchmark:
    
    ```ruby
    loop {
      puts Benchmark.measure {
        ary = [1,2,3,4]
        1_000_000.times {
          ary.each(&:object_id)
        }
      }
    }
    ```
    
    Before:
    
    ```
      1.270000   0.070000   1.340000 (  0.710043)
      0.640000   0.020000   0.660000 (  0.511692)
      0.470000   0.000000   0.470000 (  0.460667)
      0.490000   0.010000   0.500000 (  0.480732)
      0.470000   0.000000   0.470000 (  0.462888)
    ```
    
    Just the dummy binding optimization:
    
    ```
      1.210000   0.070000   1.280000 (  0.660924)
      0.540000   0.020000   0.560000 (  0.432614)
      0.430000   0.000000   0.430000 (  0.422502)
      0.430000   0.000000   0.430000 (  0.416549)
      0.410000   0.010000   0.420000 (  0.412461)
    ```
    
    And with proc caching:
    
    ```
      0.890000   0.060000   0.950000 (  0.456065)
      0.410000   0.020000   0.430000 (  0.279023)
      0.290000   0.000000   0.290000 (  0.282117)
      0.300000   0.010000   0.310000 (  0.288516)
      0.270000   0.000000   0.270000 (  0.270100)
    ```
    headius committed Dec 28, 2015
    1
    Copy the full SHA
    69662ab View commit details

Commits on Dec 30, 2015

  1. Copy the full SHA
    5941f37 View commit details
  2. Copy the full SHA
    813a222 View commit details
  3. Copy the full SHA
    5a3de79 View commit details
  4. Copy the full SHA
    8dc8ccc View commit details
  5. Copy the full SHA
    4ff8a48 View commit details
  6. [Truffle] Fix last super spec.

    eregon committed Dec 30, 2015
    Copy the full SHA
    33d388a View commit details
  7. Merge pull request #3571 from jruby/symbol_to_proc_cache

    Various literal to_proc'ed Symbol optimizations.
    headius committed Dec 30, 2015
    Copy the full SHA
    cca4a33 View commit details
  8. Copy the full SHA
    5469c41 View commit details
  9. Copy the full SHA
    f5a0db9 View commit details

Commits on Dec 31, 2015

  1. Fixes #3573. MRI 2.2.2 / JRuby 9.0.4.0 difference given explicit rece…

    …iver for private method call.
    enebo committed Dec 31, 2015
    Copy the full SHA
    4ff4ad4 View commit details

Commits on Jan 1, 2016

  1. Copy the full SHA
    6331a39 View commit details
  2. Copy the full SHA
    283ff44 View commit details
  3. Copy the full SHA
    96278fa View commit details
Showing with 485 additions and 306 deletions.
  1. +73 −65 core/src/main/java/org/jruby/RubySymbol.java
  2. +22 −12 core/src/main/java/org/jruby/ir/IRBuilder.java
  3. +1 −0 core/src/main/java/org/jruby/ir/IRVisitor.java
  4. +2 −1 core/src/main/java/org/jruby/ir/operands/OperandType.java
  5. +73 −0 core/src/main/java/org/jruby/ir/operands/SymbolProc.java
  6. +24 −0 core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
  7. +12 −1 core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
  8. +13 −0 core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
  9. +5 −0 core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
  10. +13 −0 core/src/main/java/org/jruby/runtime/Binding.java
  11. +0 −1 spec/truffle/tags/language/encoding_tags.txt
  12. +0 −1 spec/truffle/tags/language/file_tags.txt
  13. +1 −5 spec/truffle/tags/language/predefined_tags.txt
  14. +0 −1 spec/truffle/tags/language/super_tags.txt
  15. +0 −9 spec/truffle/tags/language/variables_tags.txt
  16. +5 −0 test/truffle/pe/pe.rb
  17. +9 −4 tool/jruby_eclipse
  18. +0 −3 tool/jt.rb
  19. +4 −1 truffle/pom.rb
  20. +10 −4 truffle/pom.xml
  21. +4 −0 truffle/src/main/java/org/jruby/truffle/nodes/StringCachingGuards.java
  22. +10 −1 truffle/src/main/java/org/jruby/truffle/nodes/core/BasicObjectNodes.java
  23. +3 −6 truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
  24. +1 −4 truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedMethodMissingDispatchNode.java
  25. +2 −3 truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java
  26. +0 −126 truffle/src/main/java/org/jruby/truffle/nodes/supercall/GeneralSuperReCallNode.java
  27. +56 −0 truffle/src/main/java/org/jruby/truffle/nodes/supercall/ReadSuperArgumentsNode.java
  28. +67 −0 truffle/src/main/java/org/jruby/truffle/nodes/supercall/ReadZSuperArgumentsNode.java
  29. +37 −46 .../src/main/java/org/jruby/truffle/nodes/supercall/{GeneralSuperCallNode.java → SuperCallNode.java}
  30. +1 −1 truffle/src/main/java/org/jruby/truffle/nodes/supercall/ZSuperOutsideMethodNode.java
  31. +7 −0 truffle/src/main/java/org/jruby/truffle/runtime/array/ArrayUtils.java
  32. +6 −1 truffle/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java
  33. +3 −2 truffle/src/main/java/org/jruby/truffle/runtime/loader/FeatureLoader.java
  34. +21 −8 truffle/src/main/java/org/jruby/truffle/translator/MethodTranslator.java
138 changes: 73 additions & 65 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@
import org.jruby.compiler.Constantizable;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallSite;
@@ -462,73 +463,10 @@ public IRubyObject encoding(ThreadContext context) {

@JRubyMethod
public IRubyObject to_proc(ThreadContext context) {
StaticScope scope = context.runtime.getStaticScopeFactory().getDummyScope();
final CallSite site = new FunctionalCachingCallSite(symbol);
BlockBody body = new ContextAwareBlockBody(scope, Signature.OPTIONAL) {
private IRubyObject yieldInner(ThreadContext context, RubyArray array, Block blockArg) {
if (array.isEmpty()) {
throw context.runtime.newArgumentError("no receiver given");
}

IRubyObject self = array.shift(context);

return site.call(context, self, self, array.toJavaArray(), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
RubyProc.prepareArgs(context, block.type, blockArg.getBody(), args);
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, Block blockArg) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), blockArg);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), Block.NULL_BLOCK);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), Block.NULL_BLOCK);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return site.call(context, arg0, arg0);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return site.call(context, arg0, arg0, arg1);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return site.call(context, arg0, arg0, arg1, arg2);
}

@Override
public String getFile() {
return symbol;
}

@Override
public int getLine() {
return -1;
}

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return ArgumentDescriptor.ANON_REST;
}
};
BlockBody body = new SymbolProcBody(context.runtime, symbol);

return RubyProc.newProc(context.runtime,
new Block(body, context.currentBinding()),
new Block(body, Binding.DUMMY),
Block.Type.PROC);
}

@@ -1071,4 +1009,74 @@ public static String objectToSymbolString(IRubyObject object) {
return object.convertToString().getByteList().toString();
}
}

private static class SymbolProcBody extends ContextAwareBlockBody {
private final CallSite site;

public SymbolProcBody(Ruby runtime, String symbol) {
super(runtime.getStaticScopeFactory().getDummyScope(), Signature.OPTIONAL);
this.site = new FunctionalCachingCallSite(symbol);
}

private IRubyObject yieldInner(ThreadContext context, RubyArray array, Block blockArg) {
if (array.isEmpty()) {
throw context.runtime.newArgumentError("no receiver given");
}

IRubyObject self = array.shift(context);

return site.call(context, self, self, array.toJavaArray(), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) {
RubyProc.prepareArgs(context, block.type, blockArg.getBody(), args);
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), blockArg);
}

@Override
public IRubyObject yield(ThreadContext context, Block block, IRubyObject value, Block blockArg) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), blockArg);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
return yieldInner(context, ArgsUtil.convertToRubyArray(context.runtime, value, false), Block.NULL_BLOCK);
}

@Override
protected IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
return yieldInner(context, context.runtime.newArrayNoCopyLight(args), Block.NULL_BLOCK);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
return site.call(context, arg0, arg0);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return site.call(context, arg0, arg0, arg1);
}

@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return site.call(context, arg0, arg0, arg1, arg2);
}

@Override
public String getFile() {
return site.methodName;
}

@Override
public int getLine() {
return -1;
}

@Override
public ArgumentDescriptor[] getArgumentDescriptors() {
return ArgumentDescriptor.ANON_REST;
}
}
}
34 changes: 22 additions & 12 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -2338,7 +2338,11 @@ private Operand setupCallClosure(Node node) {
case ITERNODE:
return build(node);
case BLOCKPASSNODE:
return build(((BlockPassNode)node).getBodyNode());
Node bodyNode = ((BlockPassNode)node).getBodyNode();
if (bodyNode instanceof SymbolNode) {
return new SymbolProc(((SymbolNode)bodyNode).getName(), ((SymbolNode)bodyNode).getEncoding());
}
return build(bodyNode);
default:
throw new NotCompilableException("ERROR: Encountered a method with a non-block, non-blockpass iter node at: " + node);
}
@@ -2806,10 +2810,12 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {
Label l;
Variable readerValue = createTemporaryVariable();
Variable writerValue = createTemporaryVariable();
Node receiver = opAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;

// get attr
Operand v1 = build(opAsgnNode.getReceiverNode());
addInstr(CallInstr.create(scope, readerValue, opAsgnNode.getVariableName(), v1, NO_ARGS, null));
Operand v1 = build(receiver);
addInstr(CallInstr.create(scope, callType, readerValue, opAsgnNode.getVariableName(), v1, NO_ARGS, null));

// Ex: e.val ||= n
// e.val &&= n
@@ -2820,7 +2826,7 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {

// compute value and set it
Operand v2 = build(opAsgnNode.getValueNode());
addInstr(CallInstr.create(scope, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {v2}, null));
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {v2}, null));
// It is readerValue = v2.
// readerValue = writerValue is incorrect because the assignment method
// might return something else other than the value being set!
@@ -2837,7 +2843,7 @@ public Operand buildOpAsgn(OpAsgnNode opAsgnNode) {
addInstr(CallInstr.create(scope, setValue, opAsgnNode.getOperatorName(), readerValue, new Operand[]{v2}, null));

// set attr
addInstr(CallInstr.create(scope, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {setValue}, null));
addInstr(CallInstr.create(scope, callType, writerValue, opAsgnNode.getVariableNameAsgn(), v1, new Operand[] {setValue}, null));
// Returning writerValue is incorrect becuase the assignment method
// might return something else other than the value being set!
return setValue;
@@ -2910,16 +2916,18 @@ public Operand buildOpElementAsgn(OpElementAsgnNode node) {
}

private Operand buildOpElementAsgnWith(OpElementAsgnNode opElementAsgnNode, Boolean truthy) {
Operand array = buildWithOrder(opElementAsgnNode.getReceiverNode(), opElementAsgnNode.containsVariableAssignment());
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Label endLabel = getNewLabel();
Variable elt = createTemporaryVariable();
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
addInstr(CallInstr.create(scope, elt, "[]", array, argList, null));
addInstr(CallInstr.create(scope, callType, elt, "[]", array, argList, null));
addInstr(BEQInstr.create(elt, truthy, endLabel));
Operand value = build(opElementAsgnNode.getValueNode());

argList = addArg(argList, value);
addInstr(CallInstr.create(scope, elt, "[]=", array, argList, null));
addInstr(CallInstr.create(scope, callType, elt, "[]=", array, argList, null));
addInstr(new CopyInstr(elt, value));

addInstr(new LabelInstr(endLabel));
@@ -2928,18 +2936,20 @@ private Operand buildOpElementAsgnWith(OpElementAsgnNode opElementAsgnNode, Bool

// a[i] *= n, etc. anything that is not "a[i] &&= .. or a[i] ||= .."
public Operand buildOpElementAsgnWithMethod(OpElementAsgnNode opElementAsgnNode) {
Operand array = buildWithOrder(opElementAsgnNode.getReceiverNode(), opElementAsgnNode.containsVariableAssignment());
Node receiver = opElementAsgnNode.getReceiverNode();
CallType callType = receiver instanceof SelfNode ? CallType.FUNCTIONAL : CallType.NORMAL;
Operand array = buildWithOrder(receiver, opElementAsgnNode.containsVariableAssignment());
Operand[] argList = setupCallArgs(opElementAsgnNode.getArgsNode());
Variable elt = createTemporaryVariable();
addInstr(CallInstr.create(scope, elt, "[]", array, argList, null)); // elt = a[args]
addInstr(CallInstr.create(scope, callType, elt, "[]", array, argList, null)); // elt = a[args]
Operand value = build(opElementAsgnNode.getValueNode()); // Load 'value'
String operation = opElementAsgnNode.getOperatorName();
addInstr(CallInstr.create(scope, elt, operation, elt, new Operand[] { value }, null)); // elt = elt.OPERATION(value)
addInstr(CallInstr.create(scope, callType, elt, operation, elt, new Operand[] { value }, null)); // elt = elt.OPERATION(value)
// SSS: do not load the call result into 'elt' to eliminate the RAW dependency on the call
// We already know what the result is going be .. we are just storing it back into the array
Variable tmp = createTemporaryVariable();
argList = addArg(argList, elt);
addInstr(CallInstr.create(scope, tmp, "[]=", array, argList, null)); // a[args] = elt
addInstr(CallInstr.create(scope, callType, tmp, "[]=", array, argList, null)); // a[args] = elt
return elt;
}

1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
@@ -188,6 +188,7 @@ private void error(Object object) {
public void StringLiteral(StringLiteral stringliteral) { error(stringliteral); }
public void SValue(SValue svalue) { error(svalue); }
public void Symbol(Symbol symbol) { error(symbol); }
public void SymbolProc(SymbolProc symbolproc) { error(symbolproc); }
public void TemporaryVariable(TemporaryVariable temporaryvariable) { error(temporaryvariable); }
public void TemporaryLocalVariable(TemporaryLocalVariable temporarylocalvariable) { error(temporarylocalvariable); }
public void TemporaryFloatVariable(TemporaryFloatVariable temporaryfloatvariable) { error(temporaryfloatvariable); }
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/ir/operands/OperandType.java
Original file line number Diff line number Diff line change
@@ -43,7 +43,8 @@ public enum OperandType {
WRAPPED_IR_CLOSURE((byte) 'w'),
FROZEN_STRING((byte) 'z'),
NULL_BLOCK((byte) 'o'),
FILENAME((byte) 'm')
FILENAME((byte) 'm'),
SYMBOL_PROC((byte) 'P')
;

private final byte coded;
73 changes: 73 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/SymbolProc.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.jruby.ir.operands;

import org.jcodings.Encoding;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.ThreadContext;

/**
* A literal representing proc'ified symbols, as in &:foo.
*
* Used to cache a unique and constant proc at the use site to reduce allocation and improve caching.
*/
public class SymbolProc extends ImmutableLiteral {
private final String name;
private final Encoding encoding;

public SymbolProc(String name, Encoding encoding) {
super();
this.name = name;
this.encoding = encoding;
}

@Override
public OperandType getOperandType() {
return OperandType.SYMBOL_PROC;
}

@Override
public Object createCacheObject(ThreadContext context) {
return IRRuntimeHelpers.newSymbolProc(context, name, encoding);
}

@Override
public int hashCode() {
return 47 * 7 + (int) (this.name.hashCode() ^ (this.encoding.hashCode() >>> 32));
}

@Override
public boolean equals(Object other) {
return other instanceof SymbolProc && name.equals(((SymbolProc) other).name) && encoding.equals(((SymbolProc) other).encoding);
}

@Override
public void visit(IRVisitor visitor) {
visitor.SymbolProc(this);
}

public String getName() {
return name;
}

public Encoding getEncoding() {
return encoding;
}

@Override
public void encode(IRWriterEncoder e) {
super.encode(e);
e.encode(name);
e.encode(encoding);
}

public static SymbolProc decode(IRReaderDecoder d) {
return new SymbolProc(d.decodeString(), d.decodeEncoding());
}

@Override
public String toString() {
return "SymbolProc:" + name;
}
}
24 changes: 24 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1698,4 +1698,28 @@ public static IRubyObject useBindingSelf(Binding binding) {

return self;
}

/**
* Create a new Symbol.to_proc for the given symbol name and encoding.
*
* @param context
* @param symbol
* @return
*/
@Interp
public static RubyProc newSymbolProc(ThreadContext context, String symbol, Encoding encoding) {
return (RubyProc)context.runtime.newSymbol(symbol, encoding).to_proc(context);
}

/**
* Create a new Symbol.to_proc for the given symbol name and encoding.
*
* @param context
* @param symbol
* @return
*/
@JIT
public static RubyProc newSymbolProc(ThreadContext context, String symbol, String encoding) {
return newSymbolProc(context, symbol, retrieveJCodingsEncoding(context, encoding));
}
}
13 changes: 12 additions & 1 deletion core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter.java
Original file line number Diff line number Diff line change
@@ -351,14 +351,25 @@ public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature,
* Stack required: none
*
* @param sym the symbol's string identifier
* @param encoding the symbol's encoding
*/
public abstract void pushSymbol(String sym, Encoding encoding);

/**
* Push the JRuby runtime on the stack.
* Push a Symbol.to_proc on the stack.
*
* Stack required: none
*
* @param name the symbol's string identifier
* @param encoding the symbol's encoding
*/
public abstract void pushSymbolProc(String name, Encoding encoding);

/**
* Push the JRuby runtime on the stack.
*
* Stack required: none
*/
public abstract void loadRuntime();

/**
Loading