Skip to content

Commit

Permalink
[Truffle] Optimise Array#pop()
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed May 12, 2015
1 parent ceaf178 commit 43b68cd
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ public abstract static class IndexSetNode extends ArrayCoreMethodNode {

@Child private ArrayWriteDenormalizedNode writeNode;
@Child protected ArrayReadSliceDenormalizedNode readSliceNode;
@Child private PopNode popNode;
@Child private PopOneNode popOneNode;
@Child private ToIntNode toIntNode;

private final BranchProfile tooSmallBranch = BranchProfile.create();
Expand Down Expand Up @@ -511,13 +511,13 @@ public Object setObject(VirtualFrame frame, RubyArray array, int start, int leng
} else {
writeNode.executeWrite(frame, array, begin, value);
}
if (popNode == null) {
if (popOneNode == null) {
CompilerDirectives.transferToInterpreter();
popNode = insert(ArrayNodesFactory.PopNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
popOneNode = insert(PopOneNodeGen.create(getContext(), getSourceSection(), null));
}
int popLength = length - 1 < array.getSize() ? length - 1 : array.getSize() - 1;
for (int i = 0; i < popLength; i++) { // TODO 3-15-2015 BF update when pop can pop multiple
popNode.executePop(frame, array, UndefinedPlaceholder.INSTANCE);
popOneNode.executePopOne(array);
}
return value;
}
Expand Down Expand Up @@ -612,12 +612,12 @@ public Object setOtherArray(VirtualFrame frame, RubyArray array, int start, int
final int popNum = newLength < array.getSize() ? array.getSize() - newLength : 0;

if (popNum > 0) {
if (popNode == null) {
if (popOneNode == null) {
CompilerDirectives.transferToInterpreter();
popNode = insert(ArrayNodesFactory.PopNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
popOneNode = insert(PopOneNodeGen.create(getContext(), getSourceSection(), null));
}
for (int i = 0; i < popNum; i++) { // TODO 3-28-2015 BF update to pop multiple
popNode.executePop(frame, array, UndefinedPlaceholder.INSTANCE);
popOneNode.executePopOne(array);
}
}

Expand Down Expand Up @@ -2760,101 +2760,20 @@ protected CallTarget compileFormat(RubyString format) {
public abstract static class PopNode extends ArrayCoreMethodNode {

@Child private ToIntNode toIntNode;
@Child private PopOneNode popOneNode;

public PopNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public abstract Object executePop(VirtualFrame frame, RubyArray array, Object n);

@Specialization(guards = "isNullOrEmpty(array)")
public Object popNil(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
return nil();
}

@Specialization(guards = "isIntegerFixnum(array)", rewriteOn = UnexpectedResultException.class)
public int popIntegerFixnumInBounds(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) throws UnexpectedResultException {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
throw new UnexpectedResultException(nil());
} else {
final int[] store = ((int[]) array.getStore());
final int value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
}


@Specialization(contains = "popIntegerFixnumInBounds", guards = "isIntegerFixnum(array)")
public Object popIntegerFixnum(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
return nil();
} else {
final int[] store = ((int[]) array.getStore());
final int value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
}

@Specialization(guards = "isLongFixnum(array)", rewriteOn = UnexpectedResultException.class)
public long popLongFixnumInBounds(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) throws UnexpectedResultException {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
throw new UnexpectedResultException(nil());
} else {
final long[] store = ((long[]) array.getStore());
final long value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
}

@Specialization(contains = "popLongFixnumInBounds", guards = "isLongFixnum(array)")
public Object popLongFixnum(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
return nil();
} else {
final long[] store = ((long[]) array.getStore());
final long value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
}

@Specialization(guards = "isFloat(array)", rewriteOn = UnexpectedResultException.class)
public double popFloatInBounds(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) throws UnexpectedResultException {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
throw new UnexpectedResultException(nil());
} else {
final double[] store = ((double[]) array.getStore());
final double value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
}

@Specialization(contains = "popFloatInBounds", guards = "isFloat(array)")
public Object popFloat(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
return nil();
} else {
final double[] store = ((double[]) array.getStore());
final double value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
@Specialization
public Object pop(RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
if (popOneNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
popOneNode = insert(PopOneNodeGen.create(getContext(), getEncapsulatingSourceSection(), null));
}
}

@Specialization(guards = "isObject(array)")
public Object popObject(VirtualFrame frame, RubyArray array, UndefinedPlaceholder undefinedPlaceholder) {
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, array.getSize() == 0)) {
return nil();
} else {
final Object[] store = ((Object[]) array.getStore());
final Object value = store[array.getSize() - 1];
array.setStore(store, array.getSize() - 1);
return value;
}
return popOneNode.executePopOne(array);
}

@Specialization(guards = {"isNullOrEmpty(array)","!isUndefinedPlaceholder(object)"})
Expand Down Expand Up @@ -3175,17 +3094,17 @@ public Object popObjectWithNumObj(VirtualFrame frame, RubyArray array, Object ob
}

@CoreMethod(names = "<<", raiseIfFrozenSelf = true, required = 1)
public abstract static class ShiftIntoNode extends ArrayCoreMethodNode {
public abstract static class LeftShiftNode extends ArrayCoreMethodNode {

@Child private AppendOneNode appendOneNode;

public ShiftIntoNode(RubyContext context, SourceSection sourceSection) {
public LeftShiftNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
appendOneNode = AppendOneNodeGen.create(context, sourceSection, null, null);
}

@Specialization
public RubyArray pushNullEmptySingleIntegerFixnum(RubyArray array, Object value) {
public RubyArray leftShift(RubyArray array, Object value) {
return appendOneNode.executeAppendOne(array, value);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 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.core.array;

public interface ArrayView {

Object get(int index);

class IntegerArrayView implements ArrayView {

private final int[] array;

public IntegerArrayView(int[] array) {
this.array = array;
}

@Override
public Object get(int index) {
return array[index];
}
}

class LongArrayView implements ArrayView {

private final long[] array;

public LongArrayView(long[] array) {
this.array = array;
}

@Override
public Object get(int index) {
return array[index];
}
}

class DoubleArrayView implements ArrayView {

private final double[] array;

public DoubleArrayView(double[] array) {
this.array = array;
}

@Override
public Object get(int index) {
return array[index];
}
}

class ObjectArrayView implements ArrayView {

private final Object[] array;

public ObjectArrayView(Object[] array) {
this.array = array;
}

@Override
public Object get(int index) {
return array[index];
}
}

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

public abstract class ArrayViews {

public static ArrayView.IntegerArrayView view(int[] array) {
return new ArrayView.IntegerArrayView(array);
}

public static ArrayView.LongArrayView view(long[] array) {
return new ArrayView.LongArrayView(array);
}

public static ArrayView.DoubleArrayView view(double[] array) {
return new ArrayView.DoubleArrayView(array);
}

public static ArrayView.ObjectArrayView view(Object[] array) {
return new ArrayView.ObjectArrayView(array);
}

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

import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyBasicObject;

@NodeChildren({
@NodeChild("array")
})
@ImportStatic(ArrayGuards.class)
public abstract class PopOneNode extends RubyNode {

public PopOneNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public abstract Object executePopOne(RubyArray array);

// Pop from an empty array

@Specialization(guards = "isEmpty(array)")
public RubyBasicObject popOneEmpty(RubyArray array) {
return nil();
}

// Pop from a non-empty array

@Specialization(guards = {"!isEmpty(array)", "isIntegerFixnum(array)"})
public Object popOneInteger(RubyArray array) {
return popOne(array, ArrayViews.view((int[]) array.getStore()));
}

@Specialization(guards = {"!isEmpty(array)", "isLongFixnum(array)"})
public Object popOneLong(RubyArray array) {
return popOne(array, ArrayViews.view((long[]) array.getStore()));
}

@Specialization(guards = {"!isEmpty(array)", "isFloat(array)"})
public Object popOneDouble(RubyArray array) {
return popOne(array, ArrayViews.view((double[]) array.getStore()));
}

@Specialization(guards = {"!isEmpty(array)", "isObject(array)"})
public Object popOneObject(RubyArray array) {
return popOne(array, ArrayViews.view((Object[]) array.getStore()));
}

// General implementation

private Object popOne(RubyArray array, ArrayView view) {
final int size = array.getSize();
final Object value = view.get(size - 1);
array.setSize(size - 1);
return value;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ public void setStore(Object store, int size) {
this.size = size;
}

public void setSize(int size) {
assert verifyStore(store, size);
this.size = size;
}

@CompilerDirectives.TruffleBoundary
private static Object randomizeStorageStrategy(RubyContext context, Object store, int size) {
// Use any type for empty arrays
Expand Down

0 comments on commit 43b68cd

Please sign in to comment.