diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/VectorManipulationTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/VectorManipulationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d55612022171b37a0d181ba2cae924f5046c28e3 --- /dev/null +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/VectorManipulationTest.java @@ -0,0 +1,149 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.oracle.truffle.r.nodes.access.vector; + +import static com.oracle.truffle.r.nodes.test.TestUtilities.generateInteger; +import static com.oracle.truffle.r.nodes.test.TestUtilities.generateComplex; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.oracle.truffle.r.nodes.test.TestBase; +import static com.oracle.truffle.r.nodes.test.TestUtilities.generateDouble; +import com.oracle.truffle.r.runtime.RType; +import com.oracle.truffle.r.runtime.data.NativeDataAccess; +import com.oracle.truffle.r.runtime.data.RComplex; +import com.oracle.truffle.r.runtime.data.RComplexVector; +import com.oracle.truffle.r.runtime.data.RDoubleVector; +import com.oracle.truffle.r.runtime.data.RIntVector; +import com.oracle.truffle.r.runtime.data.RVector; +import com.oracle.truffle.r.runtime.data.nodes.VectorDataReuse; +import org.junit.Test; +import org.junit.experimental.theories.Theories; +import org.junit.runner.RunWith; + +@RunWith(Theories.class) +public class VectorManipulationTest extends TestBase { + + @Test + public void dummy() { + // to make sure this file is recognized as a test + } + + @Test + public void testVectorDataReuse() { + execInContext(() -> { + // Int + RIntVector intVec = generateInteger(2, true).materialize(); + VectorDataReuse.Int intDataReuseNode = VectorDataReuse.Int.create(); + assertThat("Not temporary", intVec.isTemporary()); + int[] intData = intDataReuseNode.execute(intVec.materialize()); + assertThat("Invalid data reuse array", intVec.getInternalManagedData() == intData); + + intVec.incRefCount(); + intData = intDataReuseNode.execute(intVec.materialize()); + assertThat("Invalid data reuse array", intVec.getInternalManagedData() != intData); + assertDataContents(intVec, intData); + + NativeDataAccess.asPointer(intVec); + intVec.allocateNativeContents(); + intData = intDataReuseNode.execute(intVec.materialize()); + assertThat("Invalid data reuse array", intVec.getInternalManagedData() != intData); + assertDataContents(intVec, intData); + + intVec = generateInteger(2, true).materialize(); + assertThat("Not temporary", intVec.isTemporary()); + NativeDataAccess.asPointer(intVec); + intVec.allocateNativeContents(); + assertThat("Not a native mirror", intVec.getInternalManagedData() == null); + intData = intDataReuseNode.execute(intVec.materialize()); + assertThat("Invalid data reuse array", intVec.getInternalManagedData() != intData); + assertDataContents(intVec, intData); + + // Double + RDoubleVector doubleVector = generateDouble(2, true).materialize(); + VectorDataReuse.Double doubleDataReuseNode = VectorDataReuse.Double.create(); + assertThat("Not temporary", doubleVector.isTemporary()); + double[] doubleData = doubleDataReuseNode.execute(doubleVector.materialize()); + assertThat("Invalid data reuse array", doubleVector.getInternalManagedData() == doubleData); + + doubleVector.incRefCount(); + doubleData = doubleDataReuseNode.execute(doubleVector.materialize()); + assertThat("Invalid data reuse array", doubleVector.getInternalManagedData() != doubleData); + assertDataContents(doubleVector, doubleData); + + NativeDataAccess.asPointer(doubleVector); + doubleVector.allocateNativeContents(); + doubleData = doubleDataReuseNode.execute(doubleVector.materialize()); + assertThat("Invalid data reuse array", doubleVector.getInternalManagedData() != doubleData); + assertDataContents(doubleVector, doubleData); + + doubleVector = generateDouble(2, true).materialize(); + assertThat("Not temporary", doubleVector.isTemporary()); + NativeDataAccess.asPointer(doubleVector); + doubleVector.allocateNativeContents(); + assertThat("Not a native mirror", doubleVector.getInternalManagedData() == null); + doubleData = doubleDataReuseNode.execute(doubleVector.materialize()); + assertThat("Invalid data reuse array", doubleVector.getInternalManagedData() != doubleData); + assertDataContents(doubleVector, doubleData); + + // Complex + RComplexVector complexVector = generateComplex(2, true).materialize(); + VectorDataReuse.Complex complexDataReuseNode = VectorDataReuse.Complex.create(); + assertThat("Not temporary", complexVector.isTemporary()); + double[] complexData = complexDataReuseNode.execute(complexVector.materialize()); + assertThat("Invalid data reuse array", complexVector.getInternalManagedData() == complexData); + + complexVector.incRefCount(); + complexData = complexDataReuseNode.execute(complexVector.materialize()); + assertThat("Invalid data reuse array", complexVector.getInternalManagedData() != complexData); + assertDataContents(complexVector, complexData); + + NativeDataAccess.asPointer(complexVector); + complexVector.allocateNativeContents(); + complexData = complexDataReuseNode.execute(complexVector.materialize()); + assertThat("Invalid data reuse array", complexVector.getInternalManagedData() != complexData); + assertDataContents(complexVector, complexData); + + complexVector = generateComplex(2, true).materialize(); + assertThat("Not temporary", complexVector.isTemporary()); + NativeDataAccess.asPointer(complexVector); + complexVector.allocateNativeContents(); + assertThat("Not a native mirror", complexVector.getInternalManagedData() == null); + complexData = complexDataReuseNode.execute(complexVector.materialize()); + assertThat("Invalid data reuse array", complexVector.getInternalManagedData() != complexData); + assertDataContents(complexVector, complexData); + + return null; + }); + } + + private <ArrayT> void assertDataContents(RVector<ArrayT> vec, ArrayT arr) { + int len = vec.getLength(); + RType type = vec.getRType(); + for (int i = 0; i < len; i++) { + Object expected = vec.getDataAtAsObject(i); + Object tested; + switch (type) { + case Integer: + tested = ((int[]) arr)[i]; + break; + case Double: + tested = ((double[]) arr)[i]; + break; + case Complex: + tested = RComplex.valueOf(((double[]) arr)[2 * i], ((double[]) arr)[2 * i + 1]); + break; + case List: + tested = ((Object[]) arr)[i]; + break; + default: + throw new AssertionError("Type check not implemented yet."); + } + assertThat("Values differ at index=" + i + ", expected=" + expected + ", tested=" + tested, + (expected == null && tested == null) || (expected != null && expected.equals(tested))); + } + } + +} diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java index 17af249bd3ec7505813672ff1eca0f11d8cfc6e2..8c6d9713706eca4009147c19f793dcaaefd83ff7 100644 --- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java +++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/TestUtilities.java @@ -39,6 +39,7 @@ import com.oracle.truffle.r.runtime.context.TruffleRLanguage; import com.oracle.truffle.r.runtime.data.RComplex; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.data.model.RAbstractComplexVector; import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; @@ -123,7 +124,7 @@ public class TestUtilities { return RDataFactory.createDoubleVector(array, complete || !complete && size < 3); } - public static RAbstractVector generateComplex(int size, boolean complete) { + public static RAbstractComplexVector generateComplex(int size, boolean complete) { double[] array = new double[size << 1]; for (int i = 0; i < size; i++) { boolean useNA = !complete && i % (NA_INDEX + 1) == NA_INDEX; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java index ab9c0f0439bf4a9b41e4a692b41e6a4f572949cd..a964a37621582dbad757b7d37e7d630c11b52bd5 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java @@ -365,6 +365,15 @@ public final class NativeDataAccess { return data; } + public static double[] copyComplexNativeData(Object mirrorObj) { + NativeMirror mirror = (NativeMirror) mirrorObj; + long address = mirror.dataAddress; + assert address != 0; + double[] data = new double[(int) (mirror.length << 1)]; + UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, data.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + return data; + } + public static int[] copyIntNativeData(Object mirrorObj) { NativeMirror mirror = (NativeMirror) mirrorObj; long address = mirror.dataAddress; diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/nodes/VectorDataReuse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/nodes/VectorDataReuse.java new file mode 100644 index 0000000000000000000000000000000000000000..68a433909f60c10efb47ef0b0d4812781136e502 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/nodes/VectorDataReuse.java @@ -0,0 +1,125 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.oracle.truffle.r.runtime.data.nodes; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.data.NativeDataAccess; +import com.oracle.truffle.r.runtime.data.RComplexVector; +import com.oracle.truffle.r.runtime.data.RDoubleVector; +import com.oracle.truffle.r.runtime.data.RIntVector; +import com.oracle.truffle.r.runtime.data.RList; +import java.util.Arrays; + +/** + * Nodes contained in this class allow to reuse an internal data array of a vector which is + * temporary (has zero refCount) and its data are not natively held. Otherwise a copy of the data + * array is returned. + */ +public class VectorDataReuse { + + public abstract static class Int extends Node { + + public abstract int[] execute(RIntVector vector); + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "vec.isTemporary()"}) + protected int[] doManagedTempRVector(RIntVector vec) { + return vec.getInternalManagedData(); + } + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "!vec.isTemporary()"}) + protected int[] doManagedRVector(RIntVector vec) { + int[] data = vec.getInternalManagedData(); + return Arrays.copyOf(data, data.length); + } + + @Specialization(guards = "vec.hasNativeMemoryData()") + protected int[] doNativeDataRVector(RIntVector vec) { + return NativeDataAccess.copyIntNativeData(vec.getNativeMirror()); + } + + public static Int create() { + return VectorDataReuseFactory.IntNodeGen.create(); + } + } + + public abstract static class Double extends Node { + + public abstract double[] execute(RDoubleVector vector); + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "vec.isTemporary()"}) + protected double[] doManagedTempRVector(RDoubleVector vec) { + return vec.getInternalManagedData(); + } + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "!vec.isTemporary()"}) + protected double[] doManagedRVector(RDoubleVector vec) { + double[] data = vec.getInternalManagedData(); + return Arrays.copyOf(data, data.length); + } + + @Specialization(guards = "vec.hasNativeMemoryData()") + protected double[] doNativeDataRVector(RDoubleVector vec) { + return NativeDataAccess.copyDoubleNativeData(vec.getNativeMirror()); + } + + public static Double create() { + return VectorDataReuseFactory.DoubleNodeGen.create(); + } + } + + public abstract static class Complex extends Node { + + public abstract double[] execute(RComplexVector vector); + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "vec.isTemporary()"}) + protected double[] doManagedTempRVector(RComplexVector vec) { + return vec.getInternalManagedData(); + } + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "!vec.isTemporary()"}) + protected double[] doManagedRVector(RComplexVector vec) { + double[] data = vec.getInternalManagedData(); + return Arrays.copyOf(data, data.length); + } + + @Specialization(guards = "vec.hasNativeMemoryData()") + protected double[] doNativeDataRVector(RComplexVector vec) { + return NativeDataAccess.copyComplexNativeData(vec.getNativeMirror()); + } + + public static Complex create() { + return VectorDataReuseFactory.ComplexNodeGen.create(); + } + } + + public abstract static class ListData extends Node { + + public abstract Object[] execute(RList vector); + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "vec.isTemporary()"}) + protected Object[] doManagedTempRVector(RList vec) { + return vec.getInternalManagedData(); + } + + @Specialization(guards = {"!vec.hasNativeMemoryData()", "!vec.isTemporary()"}) + protected Object[] doManagedRVector(RList vec) { + Object[] data = vec.getInternalManagedData(); + return Arrays.copyOf(data, data.length); + } + + @Specialization(guards = "vec.hasNativeMemoryData()") + protected Object[] doNativeDataRVector(@SuppressWarnings("unused") RList vec) { + throw RInternalError.shouldNotReachHere("list cannot have native memory"); + } + + public static ListData create() { + return VectorDataReuseFactory.ListDataNodeGen.create(); + } + } +}