Skip to content

Commit

Permalink
[Truffle] Optimize dispatch for singleton objects.
Browse files Browse the repository at this point in the history
* Since they are singleton, checking against the object itself is the same
  as checking against its metaclass. Compared to checking the Shape,
  it is actually a wider check as the Shape could change.
eregon committed Aug 28, 2015

Unverified

This user has not yet uploaded their public signing key.
1 parent 554f1c0 commit e94c408
Showing 2 changed files with 166 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2014, 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.dispatch;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import com.oracle.truffle.api.object.DynamicObject;

import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.methods.InternalMethod;

/**
* Like {@link CachedBoxedDispatchNode}, but on singleton objects.
* Checking identity of the DynamicObject is therefore faster than reading the Shape and checking the Shape identity.
*/
public class CachedSingletonDispatchNode extends CachedDispatchNode {

private final DynamicObject expectedReceiver;
private final Assumption unmodifiedAssumption;

private final InternalMethod method;
@Child private DirectCallNode callNode;
@Child private IndirectCallNode indirectCallNode;

public CachedSingletonDispatchNode(
RubyContext context,
Object cachedName,
DispatchNode next,
DynamicObject expectedReceiver,
DynamicObject expectedClass,
InternalMethod method,
boolean indirect,
DispatchAction dispatchAction) {
super(context, cachedName, next, indirect, dispatchAction);

assert RubyGuards.isRubyClass(expectedClass);

this.expectedReceiver = expectedReceiver;
this.unmodifiedAssumption = Layouts.MODULE.getFields(expectedClass).getUnmodifiedAssumption();
this.next = next;
this.method = method;

if (method != null) {
if (indirect) {
indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
} else {
callNode = Truffle.getRuntime().createDirectCallNode(method.getCallTarget());

if ((callNode.isCallTargetCloningAllowed() && method.getSharedMethodInfo().shouldAlwaysSplit())
|| (method.getDeclaringModule() != null
&& Layouts.MODULE.getFields(method.getDeclaringModule()).getName().equals("TruffleInterop"))) {
insert(callNode);
callNode.cloneCallTarget();
}
}
}
}

@Override
public boolean guard(Object methodName, Object receiver) {
return guardName(methodName) &&
receiver == expectedReceiver;
}

@Override
public Object executeDispatch(
VirtualFrame frame,
Object receiverObject,
Object methodName,
Object blockObject,
Object argumentsObjects) {
if (!guard(methodName, receiverObject)) {
return next.executeDispatch(
frame,
receiverObject,
methodName,
blockObject,
argumentsObjects);
}

// Check the class has not been modified

try {
unmodifiedAssumption.check();
} catch (InvalidAssumptionException e) {
return resetAndDispatch(
frame,
receiverObject,
methodName,
(DynamicObject) blockObject,
argumentsObjects,
"class modified");
}

switch (getDispatchAction()) {
case CALL_METHOD: {
if (isIndirect()) {
return indirectCallNode.call(
frame,
method.getCallTarget(),
RubyArguments.pack(
method,
method.getDeclarationFrame(),
expectedReceiver,
(DynamicObject) blockObject,
(Object[]) argumentsObjects));
} else {
return callNode.call(
frame,
RubyArguments.pack(
method,
method.getDeclarationFrame(),
expectedReceiver,
(DynamicObject) blockObject,
(Object[]) argumentsObjects));
}
}

case RESPOND_TO_METHOD:
return true;

default:
throw new UnsupportedOperationException();
}
}

@Override
public String toString() {
return String.format("CachedBoxedDispatchNode(:%s, %s@%x, %s)",
getCachedNameAsSymbol().toString(),
expectedReceiver, expectedReceiver.hashCode(),
method == null ? "null" : method.toString());
}

public boolean couldOptimizeKeywordArguments() {
// TODO CS 18-Apr-15 doesn't seem to work with Truffle?
return false; //method.getSharedMethodInfo().getArity().getKeywordArguments() != null && next instanceof UnresolvedDispatchNode;
}

public InternalMethod getMethod() {
return method;
}

public Assumption getUnmodifiedAssumption() {
return unmodifiedAssumption;
}
}
Original file line number Diff line number Diff line change
@@ -196,11 +196,15 @@ private DispatchNode doDynamicObject(
return createMethodMissingNode(first, methodName, receiverObject);
}

final DynamicObject receiverMetaClass = getContext().getCoreLibrary().getMetaClass(receiverObject);
if (RubyGuards.isRubySymbol(receiverObject)) {
return new CachedBoxedSymbolDispatchNode(getContext(), methodName, first, method, indirect, getDispatchAction());
} else if (Layouts.CLASS.getIsSingleton(receiverMetaClass)) {
return new CachedSingletonDispatchNode(getContext(), methodName, first, ((DynamicObject) receiverObject),
receiverMetaClass, method, indirect, getDispatchAction());
} else {
return new CachedBoxedDispatchNode(getContext(), methodName, first, ((DynamicObject) receiverObject).getShape(),
getContext().getCoreLibrary().getMetaClass(receiverObject), method, indirect, getDispatchAction());
receiverMetaClass, method, indirect, getDispatchAction());
}
}

0 comments on commit e94c408

Please sign in to comment.