From 9b48c14464d8443e432538d2ef220eb4c394d203 Mon Sep 17 00:00:00 2001
From: Miloslav Metelka <miloslav.metelka@oracle.com>
Date: Wed, 17 Jan 2018 20:54:12 +0100
Subject: [PATCH] Implemented VectorDataReuse nodes.

---
 .../access/vector/VectorManipulationTest.java | 149 ++++++++++++++++++
 .../truffle/r/nodes/test/TestUtilities.java   |   3 +-
 .../r/runtime/data/NativeDataAccess.java      |   9 ++
 .../r/runtime/data/nodes/VectorDataReuse.java | 125 +++++++++++++++
 4 files changed, 285 insertions(+), 1 deletion(-)
 create mode 100644 com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/access/vector/VectorManipulationTest.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/nodes/VectorDataReuse.java

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 0000000000..d556120221
--- /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 17af249bd3..8c6d971370 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 ab9c0f0439..a964a37621 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 0000000000..68a433909f
--- /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();
+        }
+    }
+}
-- 
GitLab