Skip to content

Commit

Permalink
[Truffle] Rewrite ArrayWriteNormalizedNode and helper nodes to use Ar…
Browse files Browse the repository at this point in the history
…rayStrategy.

* Allow ArrayStrategy#generalize() to be called with an identical strategy
  so that generated code can order as it wants the @cached instantiation.
eregon committed Apr 13, 2016
1 parent c448aa7 commit 06ff2ad
Showing 4 changed files with 100 additions and 271 deletions.
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
*/
package org.jruby.truffle.core.array;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
@@ -20,69 +21,32 @@
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.language.RubyNode;

import java.util.Arrays;

@NodeChildren({
@NodeChild(value="array", type=RubyNode.class),
@NodeChild(value="requiredCapacity", type=RubyNode.class)
@NodeChild(value = "array", type = RubyNode.class),
@NodeChild(value = "requiredCapacity", type = RubyNode.class)
})
@ImportStatic(ArrayGuards.class)
public abstract class ArrayEnsureCapacityNode extends RubyNode {

private final ConditionProfile allocateProfile = ConditionProfile.createCountingProfile();

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

public abstract Object executeEnsureCapacity(DynamicObject array, int requiredCapacity);

@Specialization(guards = "isIntArray(array)")
public boolean ensureCapacityInt(DynamicObject array, int requiredCapacity) {
final int[] store = (int[]) Layouts.ARRAY.getStore(array);

if (allocateProfile.profile(store.length < requiredCapacity)) {
Layouts.ARRAY.setStore(array, Arrays.copyOf(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity)));
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return true;
} else {
return false;
}
public static ArrayEnsureCapacityNode create(RubyContext context) {
return ArrayEnsureCapacityNodeGen.create(context, null, null, null);
}

@Specialization(guards = "isLongArray(array)")
public boolean ensureCapacityLong(DynamicObject array, int requiredCapacity) {
final long[] store = (long[]) Layouts.ARRAY.getStore(array);

if (allocateProfile.profile(store.length < requiredCapacity)) {
Layouts.ARRAY.setStore(array, Arrays.copyOf(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity)));
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return true;
} else {
return false;
}
}

@Specialization(guards = "isDoubleArray(array)")
public boolean ensureCapacityDouble(DynamicObject array, int requiredCapacity) {
final double[] store = (double[]) Layouts.ARRAY.getStore(array);

if (allocateProfile.profile(store.length < requiredCapacity)) {
Layouts.ARRAY.setStore(array, Arrays.copyOf(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity)));
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return true;
} else {
return false;
}
}
public abstract Object executeEnsureCapacity(DynamicObject array, int requiredCapacity);

@Specialization(guards = "isObjectArray(array)")
public boolean ensureCapacityObject(DynamicObject array, int requiredCapacity) {
final Object[] store = (Object[]) Layouts.ARRAY.getStore(array);
@Specialization(guards = "strategy.matches(array)", limit = "ARRAY_STRATEGIES")
public boolean ensureCapacity(DynamicObject array, int requiredCapacity,
@Cached("of(array)") ArrayStrategy strategy,
@Cached("createCountingProfile()") ConditionProfile extendProfile) {
final ArrayMirror mirror = strategy.newMirror(array);

if (allocateProfile.profile(store.length < requiredCapacity)) {
Layouts.ARRAY.setStore(array, ArrayUtils.grow(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity)));
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
if (extendProfile.profile(mirror.getLength() < requiredCapacity)) {
final int capacity = ArrayUtils.capacity(getContext(), mirror.getLength(), requiredCapacity);
Layouts.ARRAY.setStore(array, mirror.copyArrayAndMirror(capacity).getArray());
return true;
} else {
return false;
Original file line number Diff line number Diff line change
@@ -9,11 +9,13 @@
*/
package org.jruby.truffle.core.array;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
@@ -30,34 +32,34 @@ public ArrayGeneralizeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public abstract Object executeGeneralize(DynamicObject array, int requiredCapacity);

@Specialization(guards = "isNullArray(array)")
public DynamicObject generalizeNull(DynamicObject array, int requiredCapacity) {
Object store = new Object[requiredCapacity];
Layouts.ARRAY.setStore(array, store);
return array;
public static ArrayGeneralizeNode create(RubyContext context) {
return ArrayGeneralizeNodeGen.create(context, null, null, null);
}

@Specialization(guards = "isIntArray(array)")
public DynamicObject generalizeInt(DynamicObject array, int requiredCapacity) {
final int[] store = (int[]) Layouts.ARRAY.getStore(array);
Layouts.ARRAY.setStore(array, ArrayUtils.boxExtra(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity) - store.length));
return array;
}
public abstract Object[] executeGeneralize(DynamicObject array, int requiredCapacity);

@Specialization(guards = "isLongArray(array)")
public DynamicObject generalizeLong(DynamicObject array, int requiredCapacity) {
final long[] store = (long[]) Layouts.ARRAY.getStore(array);
Layouts.ARRAY.setStore(array, ArrayUtils.boxExtra(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity) - store.length));
return array;
@Specialization(guards = "isNullArray(array)")
public Object[] generalizeNull(DynamicObject array, int requiredCapacity) {
Object[] store = new Object[requiredCapacity];
Layouts.ARRAY.setStore(array, store);
return store;
}

@Specialization(guards = "isDoubleArray(array)")
public DynamicObject generalizeDouble(DynamicObject array, int requiredCapacity) {
final double[] store = (double[]) Layouts.ARRAY.getStore(array);
Layouts.ARRAY.setStore(array, ArrayUtils.boxExtra(store, ArrayUtils.capacity(getContext(), store.length, requiredCapacity) - store.length));
return array;
@Specialization(guards = "strategy.matches(array)", limit = "ARRAY_STRATEGIES")
public Object[] generalize(DynamicObject array, int requiredCapacity,
@Cached("of(array)") ArrayStrategy strategy,
@Cached("createCountingProfile()") ConditionProfile extendProfile) {
assert !ArrayGuards.isObjectArray(array);
final ArrayMirror mirror = strategy.newMirror(array);
final int capacity;
if (extendProfile.profile(mirror.getLength() < requiredCapacity)) {
capacity = ArrayUtils.capacity(getContext(), mirror.getLength(), requiredCapacity);
} else {
capacity = mirror.getLength();
}
final Object[] store = mirror.getBoxedCopy(capacity);
Layouts.ARRAY.setStore(array, store);
return store;
}

}
Original file line number Diff line number Diff line change
@@ -31,7 +31,10 @@ public boolean specializesFor(Object value) {
public abstract String toString();

public ArrayStrategy generalize(ArrayStrategy other) {
generalizeCheck(other);
CompilerAsserts.neverPartOfCompilation();
if (other == this) {
return this;
}
for (ArrayStrategy generalized : TYPE_STRATEGIES) {
if (generalized.canStore(type()) && generalized.canStore(other.type())) {
return generalized;
@@ -46,12 +49,6 @@ public ArrayStrategy generalizeFor(Object value) {

// Helpers

protected void generalizeCheck(ArrayStrategy other) {
CompilerAsserts.neverPartOfCompilation();
assert other != this;
assert !canStore(other.type());
}

protected static RuntimeException unsupported() {
return new UnsupportedOperationException();
}
@@ -137,8 +134,10 @@ public boolean matches(DynamicObject array) {

@Override
public ArrayStrategy generalize(ArrayStrategy other) {
generalizeCheck(other);
if (other == LongArrayStrategy.INSTANCE) {
CompilerAsserts.neverPartOfCompilation();
if (other == this) {
return this;
} else if (other == LongArrayStrategy.INSTANCE) {
return LongArrayStrategy.INSTANCE;
} else {
return IntToObjectGeneralizationArrayStrategy.INSTANCE;
Original file line number Diff line number Diff line change
@@ -9,6 +9,9 @@
*/
package org.jruby.truffle.core.array;

import static org.jruby.truffle.core.array.ArrayHelpers.getSize;
import static org.jruby.truffle.core.array.ArrayHelpers.setStoreAndSize;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
@@ -27,71 +30,26 @@
@ImportStatic(ArrayGuards.class)
public abstract class ArrayWriteNormalizedNode extends RubyNode {

@Child private ArrayEnsureCapacityNode ensureCapacityNode;
@Child private ArrayGeneralizeNode generalizeNode;

public ArrayWriteNormalizedNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);

// TODO CS 9-Feb-15 make this lazy later on
ensureCapacityNode = ArrayEnsureCapacityNodeGen.create(context, sourceSection, null, null);
generalizeNode = ArrayGeneralizeNodeGen.create(context, sourceSection, null, null);
}

public abstract Object executeWrite(DynamicObject array, int index, Object value);

// Writing at index 0 into a null array creates a new array of the most specific type

@Specialization(
guards = { "isNullArray(array)", "index == 0" }
)
public boolean writeNull0(DynamicObject array, int index, boolean value) {
Layouts.ARRAY.setStore(array, new Object[]{value});
Layouts.ARRAY.setSize(array, 1);
return value;
}

@Specialization(
guards = { "isNullArray(array)", "index == 0" }
)
public int writeNull0(DynamicObject array, int index, int value) {
Layouts.ARRAY.setStore(array, new int[]{value});
Layouts.ARRAY.setSize(array, 1);
return value;
}

@Specialization(
guards = { "isNullArray(array)", "index == 0" }
)
public long writeNull0(DynamicObject array, int index, long value) {
Layouts.ARRAY.setStore(array, new long[]{value});
Layouts.ARRAY.setSize(array, 1);
return value;
}

@Specialization(
guards = { "isNullArray(array)", "index == 0" }
)
public double writeNull0(DynamicObject array, int index, double value) {
Layouts.ARRAY.setStore(array, new double[]{value});
Layouts.ARRAY.setSize(array, 1);
return value;
}

@Specialization(
guards = { "isNullArray(array)", "index == 0" }
)
public DynamicObject writeNull0(DynamicObject array, int index, DynamicObject value) {
Layouts.ARRAY.setStore(array, new Object[]{value});
Layouts.ARRAY.setSize(array, 1);
@Specialization(guards = { "isNullArray(array)", "index == 0", "strategy.specializesFor(value)" }, limit = "ARRAY_STRATEGIES")
public Object writeNull0(DynamicObject array, int index, Object value,
@Cached("forValue(value)") ArrayStrategy strategy) {
final ArrayMirror storeMirror = strategy.newArray(1);
storeMirror.set(0, value);
setStoreAndSize(array, storeMirror.getArray(), 1);
return value;
}

// Writing beyond index 0 in a null array creates an Object[] as we need to fill the rest with nil

@Specialization(
guards = { "isNullArray(array)", "index != 0" }
)
@Specialization(guards = { "isNullArray(array)", "index != 0" })
public Object writeNullBeyond(DynamicObject array, int index, Object value) {
final Object[] store = new Object[index + 1];

@@ -100,172 +58,78 @@ public Object writeNullBeyond(DynamicObject array, int index, Object value) {
}

store[index] = value;
Layouts.ARRAY.setStore(array, store);
Layouts.ARRAY.setSize(array, store.length);
setStoreAndSize(array, store, store.length);
return value;
}

// Writing within an existing array with a compatible type

@Specialization(
guards = { "isIntArray(array)", "isInBounds(array, index)" }
)
public int writeWithin(DynamicObject array, int index, int value) {
final int[] store = (int[]) Layouts.ARRAY.getStore(array);
store[index] = value;
return value;
}

@Specialization(
guards = { "isLongArray(array)", "isInBounds(array, index)" }
)
public long writeWithin(DynamicObject array, int index, long value) {
final long[] store = (long[]) Layouts.ARRAY.getStore(array);
store[index] = value;
return value;
}

@Specialization(
guards = { "isDoubleArray(array)", "isInBounds(array, index)" }
)
public double writeWithin(DynamicObject array, int index, double value) {
final double[] store = (double[]) Layouts.ARRAY.getStore(array);
store[index] = value;
return value;
}

@Specialization(
guards = { "isObjectArray(array)", "isInBounds(array, index)" }
)
public Object writeWithin(DynamicObject array, int index, Object value) {
final Object[] store = (Object[]) Layouts.ARRAY.getStore(array);
store[index] = value;
@Specialization(guards = {
"isInBounds(array, index)", "strategy.matches(array)", "strategy.accepts(value)"
}, limit = "ARRAY_STRATEGIES")
public Object writeWithin(DynamicObject array, int index, Object value,
@Cached("of(array, value)") ArrayStrategy strategy) {
strategy.newMirror(array).set(index, value);
return value;
}

// Writing within an existing array with an incompatible type - need to generalise

@Specialization(
guards = { "isIntArray(array)", "isInBounds(array, index)" }
)
public long writeWithinInt(DynamicObject array, int index, long value) {
final int[] intStore = (int[]) Layouts.ARRAY.getStore(array);
final long[] longStore = new long[Layouts.ARRAY.getSize(array)];

for (int n = 0; n < Layouts.ARRAY.getSize(array); n++) {
longStore[n] = intStore[n];
}

longStore[index] = value;
Layouts.ARRAY.setStore(array, longStore);
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return value;
}

@Specialization(
guards = { "isIntArray(array)", "isInBounds(array, index)", "!isInteger(value)", "!isLong(value)" }
)
public Object writeWithinInt(DynamicObject array, int index, Object value) {
final Object[] objectStore = ArrayUtils.box((int[]) Layouts.ARRAY.getStore(array));
objectStore[index] = value;
Layouts.ARRAY.setStore(array, objectStore);
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return value;
}

@Specialization(
guards = { "isLongArray(array)", "isInBounds(array, index)", "!isInteger(value)", "!isLong(value)" }
)
public Object writeWithinLong(DynamicObject array, int index, Object value) {
final Object[] objectStore = ArrayUtils.box((long[]) Layouts.ARRAY.getStore(array));
objectStore[index] = value;
Layouts.ARRAY.setStore(array, objectStore);
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
return value;
}

@Specialization(
guards = { "isDoubleArray(array)", "isInBounds(array, index)", "!isDouble(value)" }
)
public Object writeWithinDouble(DynamicObject array, int index, Object value) {
final Object[] objectStore = ArrayUtils.box((double[]) Layouts.ARRAY.getStore(array));
objectStore[index] = value;
Layouts.ARRAY.setStore(array, objectStore);
Layouts.ARRAY.setSize(array, Layouts.ARRAY.getSize(array));
@Specialization(guards = {
"isInBounds(array, index)", "currentStrategy.matches(array)", "!currentStrategy.accepts(array)", "generalizedStrategy.accepts(value)",
}, limit = "ARRAY_STRATEGIES")
public Object writeWithinGeneralize(DynamicObject array, int index, Object value,
@Cached("of(array)") ArrayStrategy currentStrategy,
@Cached("currentStrategy.generalizeFor(value)") ArrayStrategy generalizedStrategy) {
final int size = getSize(array);
final ArrayMirror currentMirror = currentStrategy.newMirror(array);
final ArrayMirror storeMirror = generalizedStrategy.newArray(currentMirror.getLength());
currentMirror.copyTo(storeMirror, 0, 0, size);
storeMirror.set(index, value);
Layouts.ARRAY.setStore(array, storeMirror.getArray());
return value;
}

// Extending an array of compatible type by just one

@Specialization(
guards = { "isIntArray(array)", "isExtendingByOne(array, index)" }
)
public int writeExtendByOne(DynamicObject array, int index, int value) {
ensureCapacityNode.executeEnsureCapacity(array, index + 1);
((int[]) Layouts.ARRAY.getStore(array))[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
@Specialization(guards = "isExtendingByOne(array, index)")
public Object writeExtendByOne(DynamicObject array, int index, Object value,
@Cached("createArrayAppendOneNode()") ArrayAppendOneNode appendNode) {
appendNode.executeAppendOne(array, value);
return value;
}

@Specialization(
guards = { "isLongArray(array)", "isExtendingByOne(array, index)" }
)
public long writeExtendByOne(DynamicObject array, int index, long value) {
ensureCapacityNode.executeEnsureCapacity(array, index + 1);
((long[]) Layouts.ARRAY.getStore(array))[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
return value;
protected ArrayAppendOneNode createArrayAppendOneNode() {
return ArrayAppendOneNodeGen.create(getContext(), getSourceSection(), null, null);
}

@Specialization(
guards = { "isDoubleArray(array)", "isExtendingByOne(array, index)" }
)
public double writeExtendByOne(DynamicObject array, int index, double value) {
ensureCapacityNode.executeEnsureCapacity(array, index + 1);
((double[]) Layouts.ARRAY.getStore(array))[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
return value;
}

@Specialization(
guards = { "isObjectArray(array)", "isExtendingByOne(array, index)" }
)
public Object writeExtendByOne(DynamicObject array, int index, Object value) {
ensureCapacityNode.executeEnsureCapacity(array, index + 1);
((Object[]) Layouts.ARRAY.getStore(array))[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
return value;
}
// Writing beyond the end of an array - may need to generalize to Object[] or otherwise extend

// Writing beyond the end of an array - may need to generalise to Object[] or otherwise extend

@Specialization(
guards = { "!isObjectArray(array)", "!isInBounds(array, index)", "!isExtendingByOne(array, index)" }
)
public Object writeBeyondPrimitive(DynamicObject array, int index, Object value) {
generalizeNode.executeGeneralize(array, index + 1);
final Object[] objectStore = ((Object[]) Layouts.ARRAY.getStore(array));

for (int n = Layouts.ARRAY.getSize(array); n < index; n++) {
@Specialization(guards = {
"!isObjectArray(array)", "!isInBounds(array, index)", "!isExtendingByOne(array, index)",
})
public Object writeBeyondPrimitive(DynamicObject array, int index, Object value,
@Cached("create(getContext())") ArrayGeneralizeNode generalizeNode) {
final int newSize = index + 1;
final Object[] objectStore = generalizeNode.executeGeneralize(array, newSize);
for (int n = getSize(array); n < index; n++) {
objectStore[n] = nil();
}

objectStore[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
setStoreAndSize(array, objectStore, newSize);
return value;
}

@Specialization(
guards = { "isObjectArray(array)", "!isInBounds(array, index)", "!isExtendingByOne(array, index)" }
)
public Object writeBeyondObject(DynamicObject array, int index, Object value) {
@Specialization(guards = {
"isObjectArray(array)", "!isInBounds(array, index)", "!isExtendingByOne(array, index)"
})
public Object writeBeyondObject(DynamicObject array, int index, Object value,
@Cached("create(getContext())") ArrayEnsureCapacityNode ensureCapacityNode) {
ensureCapacityNode.executeEnsureCapacity(array, index + 1);
final Object[] objectStore = ((Object[]) Layouts.ARRAY.getStore(array));

for (int n = Layouts.ARRAY.getSize(array); n < index; n++) {
for (int n = getSize(array); n < index; n++) {
objectStore[n] = nil();
}

objectStore[index] = value;
Layouts.ARRAY.setSize(array, index + 1);
return value;
@@ -274,11 +138,11 @@ public Object writeBeyondObject(DynamicObject array, int index, Object value) {
// Guards

protected static boolean isInBounds(DynamicObject array, int index) {
return index >= 0 && index < Layouts.ARRAY.getSize(array);
return index >= 0 && index < getSize(array);
}

protected static boolean isExtendingByOne(DynamicObject array, int index) {
return index == Layouts.ARRAY.getSize(array);
return index == getSize(array);
}

}

0 comments on commit 06ff2ad

Please sign in to comment.