Skip to content

Commit

Permalink
Showing 3 changed files with 385 additions and 26 deletions.
119 changes: 93 additions & 26 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/BindingNodes.java
Original file line number Diff line number Diff line change
@@ -11,12 +11,18 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.globals.GetFromThreadLocalNode;
import org.jruby.truffle.nodes.globals.WrapInThreadLocalNode;
import org.jruby.truffle.nodes.methods.locals.ReadAbstractFrameSlotNode;
import org.jruby.truffle.nodes.methods.locals.ReadAbstractFrameSlotNodeGen;
import org.jruby.truffle.nodes.methods.locals.WriteAbstractFrameSlotNode;
import org.jruby.truffle.nodes.methods.locals.WriteAbstractFrameSlotNodeGen;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyArray;
@@ -37,8 +43,6 @@ public InitializeCopyNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public Object initializeCopy(RubyBinding self, RubyBinding from) {
CompilerDirectives.transferToInterpreter();

if (self == from) {
return self;
}
@@ -66,21 +70,52 @@ public LocalVariableGetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public Object localVariableGet(RubyBinding binding, RubySymbol symbol) {
CompilerDirectives.transferToInterpreter();
@Specialization(guards = {
"!isLastLine(symbol)",
"getFrameDescriptor(binding) == cachedFrameDescriptor",
"symbol == cachedSymbol"

})
public Object localVariableGetCached(RubyBinding binding, RubySymbol symbol,
@Cached("symbol") RubySymbol cachedSymbol,
@Cached("getFrameDescriptor(binding)") FrameDescriptor cachedFrameDescriptor,
@Cached("createReadNode(findFrameSlot(cachedFrameDescriptor, symbol))") ReadAbstractFrameSlotNode readLocalVariableNode) {
return readLocalVariableNode.executeRead(binding.getFrame());
}

This comment has been minimized.

Copy link
@chrisseaton

chrisseaton May 8, 2015

Author Contributor

This is another example of the new kind of caching we need to be doing. We should go back over all our nodes in time and add inline caching like this wherever we're looking something up. In this case it should allow us to constant fold through Binding#local_variable_get/set (it should - it doesn't yet).

@nirvdrum @eregon @pitr-ch


This comment has been minimized.

Copy link
@eregon

eregon May 9, 2015

Member

Why does it not currently?

This comment has been minimized.

Copy link
@chrisseaton

chrisseaton May 9, 2015

Author Contributor

Any iteration of frames is currently a boundary https://github.com/OracleLabs/GraalVM/blob/master/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java#L163 so even if the call is inlined and the frame doesn't escape you can't get hold of it.

This comment has been minimized.

Copy link
@eregon

eregon May 11, 2015

Member

How is iterateFrames involved in the body of the method here?

This comment has been minimized.

Copy link
@chrisseaton

chrisseaton May 11, 2015

Author Contributor

Ah should have clarified - I mean if you have gotten a binding using something like Kernel#binding - that's where the problem is. A binding from a Proc might be ok.

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "!isLastLine(symbol)")
public Object localVariableGetUncached(RubyBinding binding, RubySymbol symbol) {
final MaterializedFrame frame = binding.getFrame();
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
return frame.getValue(frameSlot);
}

Object value = frame.getValue(frame.getFrameDescriptor().findFrameSlot(symbol.toString()));
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isLastLine(symbol)")
public Object localVariableGetLastLine(RubyBinding binding, RubySymbol symbol) {
final MaterializedFrame frame = binding.getFrame();
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
final Object value = frame.getValue(frameSlot);
return GetFromThreadLocalNode.get(getContext(), value);
}

// TODO(CS): temporary hack for $_
if (symbol.toString().equals("$_")) {
value = GetFromThreadLocalNode.get(getContext(), value);
}
protected FrameDescriptor getFrameDescriptor(RubyBinding binding) {
return binding.getFrame().getFrameDescriptor();
}

return value;
protected FrameSlot findFrameSlot(FrameDescriptor frameDescriptor, RubySymbol symbol) {
return frameDescriptor.findFrameSlot(symbol.toString());
}

protected ReadAbstractFrameSlotNode createReadNode(FrameSlot frameSlot) {
return ReadAbstractFrameSlotNodeGen.create(frameSlot);
}

protected boolean isLastLine(RubySymbol symbol) {
return symbol.toString() == "$_";
}

}

@CoreMethod(names = "local_variable_set", required = 2)
@@ -90,31 +125,64 @@ public LocalVariableSetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public Object localVariableSetNode(RubyBinding binding, RubySymbol symbol, Object value) {
CompilerDirectives.transferToInterpreter();
@Specialization(guards = {
"!isLastLine(symbol)",
"getFrameDescriptor(binding) == cachedFrameDescriptor",
"symbol == cachedSymbol"

This comment has been minimized.

Copy link
@nirvdrum

nirvdrum May 8, 2015

Contributor

This may just be exploiting an implementation detail, but Truffle generates the guard checks in the order they appear here. Moving this reference check to the first position could let you short-circuit in the common case of looking up two different variable names.

This comment has been minimized.

Copy link
@chrisseaton

chrisseaton May 8, 2015

Author Contributor

Fixed in 7dd17d2

})
public Object localVariableSetCached(RubyBinding binding, RubySymbol symbol, Object value,
@Cached("symbol") RubySymbol cachedSymbol,
@Cached("getFrameDescriptor(binding)") FrameDescriptor cachedFrameDescriptor,
@Cached("createWriteNode(findFrameSlot(binding, symbol))") WriteAbstractFrameSlotNode writeLocalVariableNode) {
return writeLocalVariableNode.executeWrite(binding.getFrame(), value);
}

// TODO(CS): temporary hack for $_
if (symbol.toString().equals("$_")) {
value = WrapInThreadLocalNode.wrap(getContext(), value);
}
@CompilerDirectives.TruffleBoundary
@Specialization(guards = "!isLastLine(symbol)")
public Object localVariableSetUncached(RubyBinding binding, RubySymbol symbol, Object value) {
final MaterializedFrame frame = binding.getFrame();
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
frame.setObject(frameSlot, value);
return value;
}

@CompilerDirectives.TruffleBoundary
@Specialization(guards = "isLastLine(symbol)")
public Object localVariableSetLastLine(RubyBinding binding, RubySymbol symbol, Object value) {
final MaterializedFrame frame = binding.getFrame();
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
frame.setObject(frameSlot, WrapInThreadLocalNode.wrap(getContext(), value));
return value;
}

protected FrameDescriptor getFrameDescriptor(RubyBinding binding) {
return binding.getFrame().getFrameDescriptor();
}

protected FrameSlot findFrameSlot(RubyBinding binding, RubySymbol symbol) {
final String symbolString = symbol.toString();

MaterializedFrame frame = binding.getFrame();

while (frame != null) {
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbolString);

if (frameSlot != null) {
frame.setObject(frameSlot, value);
return value;
return frameSlot;
}

frame = RubyArguments.getDeclarationFrame(frame.getArguments());
}

final FrameSlot newFrameSlot = binding.getFrame().getFrameDescriptor().addFrameSlot(symbol.toString());
binding.getFrame().setObject(newFrameSlot, value);
return value;
return binding.getFrame().getFrameDescriptor().addFrameSlot(symbolString);
}

protected WriteAbstractFrameSlotNode createWriteNode(FrameSlot frameSlot) {
return WriteAbstractFrameSlotNodeGen.create(frameSlot);
}

protected boolean isLastLine(RubySymbol symbol) {
return symbol.toString() == "$_";
}
}

@@ -125,10 +193,9 @@ public LocalVariablesNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyArray localVariables(RubyBinding binding) {
CompilerDirectives.transferToInterpreter();

final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass());

MaterializedFrame frame = binding.getFrame();
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2013, 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.methods.locals;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.Node;

public abstract class ReadAbstractFrameSlotNode extends Node {

protected final FrameSlot frameSlot;

public ReadAbstractFrameSlotNode(FrameSlot slot) {
assert slot != null;
this.frameSlot = slot;
}

public abstract Object executeRead(Frame frame);

@Specialization(rewriteOn = {FrameSlotTypeException.class})
public boolean doBoolean(Frame frame) throws FrameSlotTypeException {
return getBoolean(frame);
}

@Specialization(rewriteOn = {FrameSlotTypeException.class})
public int doFixnum(Frame frame) throws FrameSlotTypeException {
return getFixnum(frame);
}

@Specialization(rewriteOn = {FrameSlotTypeException.class})
public long doLongFixnum(Frame frame) throws FrameSlotTypeException {
return getLongFixnum(frame);
}

@Specialization(rewriteOn = {FrameSlotTypeException.class})
public double doFloat(Frame frame) throws FrameSlotTypeException {
return getFloat(frame);
}

@Specialization(rewriteOn = {FrameSlotTypeException.class})
public Object doObject(Frame frame) throws FrameSlotTypeException {
return getObject(frame);
}

@Specialization
public Object doValue(Frame frame) {
return getValue(frame);
}

public final FrameSlot getFrameSlot() {
return frameSlot;
}

@Override
public ReadAbstractFrameSlotNode copy() {
return (ReadAbstractFrameSlotNode) super.copy();
}

protected final void setBoolean(Frame frame, boolean value) {
frame.setBoolean(frameSlot, value);
}

protected final void setFixnum(Frame frame, int value) {
frame.setInt(frameSlot, value);
}

protected final void setLongFixnum(Frame frame, long value) {
frame.setLong(frameSlot, value);
}

protected final void setFloat(Frame frame, double value) {
frame.setDouble(frameSlot, value);
}

protected final void setObject(Frame frame, Object value) {
frame.setObject(frameSlot, value);
}

protected final boolean getBoolean(Frame frame) throws FrameSlotTypeException {
return frame.getBoolean(frameSlot);
}

protected final int getFixnum(Frame frame) throws FrameSlotTypeException {
return frame.getInt(frameSlot);
}

protected final long getLongFixnum(Frame frame) throws FrameSlotTypeException {
return frame.getLong(frameSlot);
}

protected final double getFloat(Frame frame) throws FrameSlotTypeException {
return frame.getDouble(frameSlot);
}

protected final Object getObject(Frame frame) throws FrameSlotTypeException {
return frame.getObject(frameSlot);
}

protected final Object getValue(Frame frame) {
return frame.getValue(frameSlot);
}

protected final boolean isBooleanKind(Frame frame) {
return isKind(FrameSlotKind.Boolean);
}

protected final boolean isFixnumKind(Frame frame) {
return isKind(FrameSlotKind.Int);
}

protected final boolean isLongFixnumKind(Frame frame) {
return isKind(FrameSlotKind.Long);
}

protected final boolean isFloatKind(Frame frame) {
return isKind(FrameSlotKind.Double);
}

protected final boolean isObjectKind(Frame frame) {
if (frameSlot.getKind() != FrameSlotKind.Object) {
CompilerDirectives.transferToInterpreter();
frameSlot.setKind(FrameSlotKind.Object);
}
return true;
}

private boolean isKind(FrameSlotKind kind) {
return frameSlot.getKind() == kind || initialSetKind(kind);
}

private boolean initialSetKind(FrameSlotKind kind) {
if (frameSlot.getKind() == FrameSlotKind.Illegal) {
CompilerDirectives.transferToInterpreter();
frameSlot.setKind(kind);
return true;
}
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (c) 2013, 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.methods.locals;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.nodes.Node;

public abstract class WriteAbstractFrameSlotNode extends Node {

protected final FrameSlot frameSlot;

public WriteAbstractFrameSlotNode(FrameSlot frameSlot) {
assert frameSlot != null;
this.frameSlot = frameSlot;
}

public abstract Object executeWrite(Frame frame, Object value);

@Specialization(guards = "isBooleanKind(frame)")
public boolean doFixnum(Frame frame, boolean value) {
setBoolean(frame, value);
return value;
}

@Specialization(guards = "isFixnumKind(frame)")
public int doFixnum(Frame frame, int value) {
setFixnum(frame, value);
return value;
}

@Specialization(guards = "isLongFixnumKind(frame)")
public long doLongFixnum(Frame frame, long value) {
setLongFixnum(frame, value);
return value;
}

@Specialization(guards = "isFloatKind(frame)")
public double doFloat(Frame frame, double value) {
setFloat(frame, value);
return value;
}

@Specialization(guards = "isObjectKind(frame)")
public Object doObject(Frame frame, Object value) {
setObject(frame, value);
return value;
}

public final FrameSlot getFrameSlot() {
return frameSlot;
}

protected final void setBoolean(Frame frame, boolean value) {
frame.setBoolean(frameSlot, value);
}

protected final void setFixnum(Frame frame, int value) {
frame.setInt(frameSlot, value);
}

protected final void setLongFixnum(Frame frame, long value) {
frame.setLong(frameSlot, value);
}

protected final void setFloat(Frame frame, double value) {
frame.setDouble(frameSlot, value);
}

protected final void setObject(Frame frame, Object value) {
frame.setObject(frameSlot, value);
}

protected final boolean getBoolean(Frame frame) throws FrameSlotTypeException {
return frame.getBoolean(frameSlot);
}

protected final int getFixnum(Frame frame) throws FrameSlotTypeException {
return frame.getInt(frameSlot);
}

protected final long getLongFixnum(Frame frame) throws FrameSlotTypeException {
return frame.getLong(frameSlot);
}

protected final double getFloat(Frame frame) throws FrameSlotTypeException {
return frame.getDouble(frameSlot);
}

protected final Object getObject(Frame frame) throws FrameSlotTypeException {
return frame.getObject(frameSlot);
}

protected final Object getValue(Frame frame) {
return frame.getValue(frameSlot);
}

protected final boolean isBooleanKind(Frame frame) {
return isKind(FrameSlotKind.Boolean);
}

protected final boolean isFixnumKind(Frame frame) {
return isKind(FrameSlotKind.Int);
}

protected final boolean isLongFixnumKind(Frame frame) {
return isKind(FrameSlotKind.Long);
}

protected final boolean isFloatKind(Frame frame) {
return isKind(FrameSlotKind.Double);
}

protected final boolean isObjectKind(Frame frame) {
if (frameSlot.getKind() != FrameSlotKind.Object) {
CompilerDirectives.transferToInterpreter();
frameSlot.setKind(FrameSlotKind.Object);
}
return true;
}

private boolean isKind(FrameSlotKind kind) {
return frameSlot.getKind() == kind || initialSetKind(kind);
}

private boolean initialSetKind(FrameSlotKind kind) {
if (frameSlot.getKind() == FrameSlotKind.Illegal) {
CompilerDirectives.transferToInterpreter();
frameSlot.setKind(kind);
return true;
}
return false;
}

}

0 comments on commit 690d554

Please sign in to comment.