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: 71fd81b1b7ce
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d91dea5b7fbe
Choose a head ref
  • 2 commits
  • 12 files changed
  • 1 contributor

Commits on Apr 12, 2016

  1. Copy the full SHA
    72866ce View commit details
  2. [Truffle] Introduce first-class Array strategies!

    * Reduces the number of specializations and duplication by ~4.
    * Add a spec to ensure all transitions are as intended.
    * Add a LongIntArrayMirror for the proper casting.
    * Make ArrayMirror.copyTo use a loop so it can be used to copy between different array types.
    eregon committed Apr 12, 2016
    Copy the full SHA
    d91dea5 View commit details
137 changes: 137 additions & 0 deletions spec/truffle/specs/truffle/array/append_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright (c) 2008 Engine Yard, Inc. All rights reserved.

# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:

# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

require_relative '../../../../ruby/spec_helper'

describe "Array#<<" do
def storage(ary)
Truffle::Primitive.array_storage(ary)
end

before :each do
@long = 1 << 52
@long_ary = [@long, @long+1, @long+2]
end

it "empty array has null storage" do
ary = []
storage(ary).should == "null"
end

it "supports transitions from null storage" do
ary = [] << 1
ary.should == [1]
storage(ary).should == "int[]"

ary = [] << @long
ary.should == [@long]
storage(ary).should == "long[]"

ary = [] << 1.33
ary.should == [1.33]
storage(ary).should == "double[]"

o = Object.new
ary = [] << o
ary.should == [o]
storage(ary).should == "Object[]"
end

it "supports adding the same type" do
ary = [1] << 2
ary.should == [1, 2]
storage(ary).should == "int[]"

ary = [@long] << @long+1
ary.should == [@long, @long+1]
storage(ary).should == "long[]"

ary = [3.14] << 3.15
ary.should == [3.14, 3.15]
storage(ary).should == "double[]"

a, b = Object.new, Object.new
ary = [a] << b
ary.should == [a, b]
storage(ary).should == "Object[]"
end

it "supports long[] << int" do
storage(@long_ary).should == "long[]"

@long_ary << 1
@long_ary.should == [@long, @long+1, @long+2, 1]
storage(@long_ary).should == "long[]"
end

it "supports int[] << long and goes to long[]" do
# Make sure long[] is chosen even if there was a int[] << Object before
2.times do |i|
setup = (i == 0)

ary = [0, 1, 2]
storage(ary).should == "int[]"

obj = (i == 0) ? Object.new : @long
ary << obj
ary.should == [0, 1, 2, obj]
storage(ary).should == (setup ? "Object[]" : "long[]")
end
end

it "supports int[] << double" do
ary = [0, 1, 2]
storage(ary).should == "int[]"

ary << 1.34
ary.should == [0, 1, 2, 1.34]
storage(ary).should == "Object[]"
end

it "supports long[] << double" do
storage(@long_ary).should == "long[]"

@long_ary << 1.34
@long_ary.should == [@long, @long+1, @long+2, 1.34]
storage(@long_ary).should == "Object[]"
end

it "supports double[] << int" do
ary = [3.14, 3.15]
storage(ary).should == "double[]"

ary << 4
ary.should == [3.14, 3.15, 4]
storage(ary).should == "Object[]"
end

it "supports Object[] << int" do
o = Object.new
ary = [o]
storage(ary).should == "Object[]"

ary << 4
ary.should == [o, 4]
storage(ary).should == "Object[]"
end
end
6 changes: 3 additions & 3 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -29,9 +29,9 @@

module Utilities

def self.graal_version
def self.truffle_version
File.foreach("#{JRUBY_DIR}/truffle/pom.rb") do |line|
if /jar 'com.oracle:truffle:(\d+\.\d+(?:-SNAPSHOT)?)'/ =~ line
if /\btruffle_version = '(\d+\.\d+(?:-SNAPSHOT)?)'/ =~ line
break $1
end
end
@@ -63,7 +63,7 @@ def self.find_graal_js

def self.jruby_eclipse?
# tool/jruby_eclipse only works on release currently
ENV["JRUBY_ECLIPSE"] == "true" && Utilities.git_branch == "master"
ENV["JRUBY_ECLIPSE"] == "true" and !truffle_version.end_with?('SNAPSHOT')
end

def self.find_ruby
Original file line number Diff line number Diff line change
@@ -36,65 +36,23 @@ public ArrayAppendOneNode(RubyContext context, SourceSection sourceSection) {

// Append into an empty array

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

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

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

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

// Append of the correct type

@Specialization(guards = "isIntArray(array)")
public DynamicObject appendOneSameType(DynamicObject array, int value,
@Cached("createBinaryProfile()") ConditionProfile extendProfile) {
appendOneSameTypeGeneric(array, ArrayReflector.reflect((int[]) Layouts.ARRAY.getStore(array)), value, extendProfile);
return array;
}

@Specialization(guards = "isLongArray(array)")
public DynamicObject appendOneSameType(DynamicObject array, long value,
@Cached("createBinaryProfile()") ConditionProfile extendProfile) {
appendOneSameTypeGeneric(array, ArrayReflector.reflect((long[]) Layouts.ARRAY.getStore(array)), value, extendProfile);
return array;
}

@Specialization(guards = "isDoubleArray(array)")
public DynamicObject appendOneSameType(DynamicObject array, double value,
@Cached("createBinaryProfile()") ConditionProfile extendProfile) {
appendOneSameTypeGeneric(array, ArrayReflector.reflect((double[]) Layouts.ARRAY.getStore(array)), value, extendProfile);
return array;
}

@Specialization(guards = "isObjectArray(array)")
@Specialization(guards = { "strategy.matches(array)", "strategy.accepts(value)" }, limit = "ARRAY_STRATEGIES")
public DynamicObject appendOneSameType(DynamicObject array, Object value,
@Cached("createBinaryProfile()") ConditionProfile extendProfile) {
appendOneSameTypeGeneric(array, ArrayReflector.reflect((Object[]) Layouts.ARRAY.getStore(array)), value, extendProfile);
return array;
}

public void appendOneSameTypeGeneric(DynamicObject array, ArrayMirror storeMirror, Object value, ConditionProfile extendProfile) {
@Cached("of(array, value)") ArrayStrategy strategy,
@Cached("createBinaryProfile()") ConditionProfile extendProfile) {
final ArrayMirror storeMirror = strategy.newMirror(array);
final int oldSize = Layouts.ARRAY.getSize(array);
final int newSize = oldSize + 1;

@@ -107,53 +65,28 @@ public void appendOneSameTypeGeneric(DynamicObject array, ArrayMirror storeMirro
storeMirror.set(oldSize, value);
Layouts.ARRAY.setSize(array, newSize);
}
}

// Append forcing a generalization from int[] to long[]

@Specialization(guards = "isIntArray(array)")
public DynamicObject appendOneLongIntoInteger(DynamicObject array, long value) {
final int oldSize = Layouts.ARRAY.getSize(array);
final int newSize = oldSize + 1;

final int[] oldStore = (int[]) Layouts.ARRAY.getStore(array);
long[] newStore = ArrayUtils.longCopyOf(oldStore, ArrayUtils.capacity(getContext(), oldStore.length, newSize));

newStore[oldSize] = value;
Layouts.ARRAY.setStore(array, newStore);
Layouts.ARRAY.setSize(array, newSize);
return array;
}

// Append forcing a generalization to Object[]
// Append forcing a generalization

@Specialization(guards = { "isIntArray(array)", "!isInteger(value)", "!isLong(value)" })
public DynamicObject appendOneGeneralizeInteger(DynamicObject array, Object value) {
appendOneGeneralizeGeneric(array, ArrayReflector.reflect((int[]) Layouts.ARRAY.getStore(array)), value);
return array;
}

@Specialization(guards = { "isLongArray(array)", "!isInteger(value)", "!isLong(value)" })
public DynamicObject appendOneGeneralizeLong(DynamicObject array, Object value) {
appendOneGeneralizeGeneric(array, ArrayReflector.reflect((long[]) Layouts.ARRAY.getStore(array)), value);
return array;
}

@Specialization(guards = { "isDoubleArray(array)", "!isDouble(value)" })
public DynamicObject appendOneGeneralizeDouble(DynamicObject array, Object value) {
appendOneGeneralizeGeneric(array, ArrayReflector.reflect((double[]) Layouts.ARRAY.getStore(array)), value);
return array;
}

public void appendOneGeneralizeGeneric(DynamicObject array, ArrayMirror storeMirror, Object value) {
@Specialization(guards = {
"currentStrategy.matches(array)", "!currentStrategy.accepts(array)", "generalizedStrategy.accepts(value)",
"currentStrategy != generalizedStrategy" }, limit = "ARRAY_STRATEGIES")
public DynamicObject appendOneGeneralize(DynamicObject array, Object value,
@Cached("of(array)") ArrayStrategy currentStrategy,
@Cached("currentStrategy.generalizeFor(value)") ArrayStrategy generalizedStrategy) {
final int oldSize = Layouts.ARRAY.getSize(array);
final int newSize = oldSize + 1;
final int oldCapacity = storeMirror.getLength();
final int newCapacity = newSize > oldCapacity ? ArrayUtils.capacityForOneMore(getContext(), storeMirror.getLength()) : oldCapacity;
Object[] newStore = storeMirror.getBoxedCopy(newCapacity);
newStore[oldSize] = value;
Layouts.ARRAY.setStore(array, newStore);
final ArrayMirror currentMirror = currentStrategy.newMirror(array);
final int oldCapacity = currentMirror.getLength();
final int newCapacity = newSize > oldCapacity ? ArrayUtils.capacityForOneMore(getContext(), oldCapacity) : oldCapacity;
final ArrayMirror storeMirror = generalizedStrategy.newArray(newCapacity);
currentMirror.copyTo(storeMirror, 0, 0, oldSize);
storeMirror.set(oldSize, value);
Layouts.ARRAY.setStore(array, storeMirror.getArray());
Layouts.ARRAY.setSize(array, newSize);
return array;
}

}
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@

public class ArrayGuards {

// Enough to handle (all types + null) * (all types + null).
public static final int ARRAY_STRATEGIES = 25;

// Storage strategies

public static boolean isNullArray(DynamicObject array) {
Loading