diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index 575c68930adf236a99572ec0b323f110a0106d19..c0e6de4901cceafa2019f46f0105cbbdd5a902f7 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -350,6 +350,7 @@ public class BasePackage extends RBuiltinPackage { add(Ls.class, LsNodeGen::create); add(MakeNames.class, MakeNamesNodeGen::create); add(MakeUnique.class, MakeUniqueNodeGen::create); + add(Mapply.class, MapplyNodeGen::create); add(MatMult.class, MatMultNodeGen::create); add(Match.class, MatchNodeGen::create); add(MatchFun.class, MatchFunNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java index 80be14922b327c6e118f9c2612b01e6fd3064f99..2bed762f38e730e726beb4dee1ef0975af53899e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/InfixEmulationFunctions.java @@ -387,6 +387,8 @@ public class InfixEmulationFunctions { private static final String NAME = "[["; + public abstract Object execute(VirtualFrame frame, RAbstractContainer x, RArgsValuesAndNames inds, RAbstractLogicalVector exactVec, RAbstractLogicalVector dropVec); + @Child private UseMethodInternalNode dcn; @Override diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java new file mode 100644 index 0000000000000000000000000000000000000000..5dd5f43d3f214c415e244bd841ed78eb64941144 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Mapply.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.truffle.r.nodes.builtin.base; + +import static com.oracle.truffle.r.runtime.RBuiltinKind.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.r.nodes.*; +import com.oracle.truffle.r.nodes.access.*; +import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode; +import com.oracle.truffle.r.nodes.access.variables.*; +import com.oracle.truffle.r.nodes.builtin.*; +import com.oracle.truffle.r.nodes.builtin.base.InfixEmulationFunctions.AccessArraySubscriptBuiltin; +import com.oracle.truffle.r.nodes.builtin.base.MapplyNodeGen.MapplyInternalNodeGen; +import com.oracle.truffle.r.nodes.function.*; +import com.oracle.truffle.r.runtime.*; +import com.oracle.truffle.r.runtime.data.*; +import com.oracle.truffle.r.runtime.data.model.*; + +/** + * Multivariate lapply. Essentially invokes + * {@code fun(dots[0][X], dots[1][X], , dots[N][X], MoreArgs)} for {@code X=1..M} where {@code M} is + * the longest vector, with the usual recycling rule. + */ +@RBuiltin(name = "mapply", kind = INTERNAL, parameterNames = {"FUN", "dots", "MoreArgs"}, splitCaller = true) +public abstract class Mapply extends RBuiltinNode { + + protected static class ElementNode extends Node { + @Child Length lengthNode; + @Child AccessArraySubscriptBuiltin indexedLoadNode; + @Child WriteVariableNode writeVectorElementNode; + private final String vectorElementName; + + ElementNode(String vectorElementName) { + this.vectorElementName = AnonymousFrameVariable.create(vectorElementName); + this.lengthNode = insert(LengthNodeGen.create(null, null, null)); + this.indexedLoadNode = insert(InfixEmulationFunctionsFactory.AccessArraySubscriptBuiltinNodeGen.create(null, null, null)); + this.writeVectorElementNode = insert(WriteVariableNode.createAnonymous(this.vectorElementName, null, Mode.REGULAR)); + } + } + + @Child private MapplyInternalNode mapply = MapplyInternalNodeGen.create(null, null, null); + + @Specialization + protected Object mApply(VirtualFrame frame, RFunction fun, RList dots, RList moreArgs) { + if (moreArgs.getLength() > 0) { + throw RError.nyi(getEncapsulatingSourceSection(), "moreArgs"); + } + Object[] result = mapply.execute(frame, dots, fun, moreArgs); + // set here else it gets overridden by the iterator evaluation + controlVisibility(); + return RDataFactory.createList(result); + } + + @SuppressWarnings("unused") + @Specialization + protected Object mApply(VirtualFrame frame, RFunction fun, RList dots, RNull moreArgs) { + return mApply(frame, fun, dots, RDataFactory.createList()); + } + + @NodeChildren({@NodeChild(type = RNode.class), @NodeChild(type = RNode.class), @NodeChild(type = RNode.class)}) + protected abstract static class MapplyInternalNode extends RNode { + + private static final String VECTOR_ELEMENT_PREFIX = "MAPPLY_VEC_ELEM_"; + private static final RLogicalVector DROP = RDataFactory.createLogicalVectorFromScalar(true); + private static final RLogicalVector EXACT = RDataFactory.createLogicalVectorFromScalar(true); + private static final ArgumentsSignature I_INDEX = ArgumentsSignature.get("i"); + private static final RArgsValuesAndNames[] INDEX_CACHE = new RArgsValuesAndNames[32]; + + static { + for (int i = 0; i < INDEX_CACHE.length; i++) { + INDEX_CACHE[i] = new RArgsValuesAndNames(new Object[]{i + 1}, I_INDEX); + } + } + + public abstract Object[] execute(VirtualFrame frame, RList dots, RFunction function, RList additionalArguments); + + @SuppressWarnings("unused") + @Specialization(limit = "5", guards = {"function.getTarget() == cachedTarget"}) + protected Object[] cachedMApply(VirtualFrame frame, RList dots, RFunction function, RList moreArgs, @Cached("function.getTarget()") RootCallTarget cachedTarget, + @Cached("createElementNodeArray(dots.getLength())") ElementNode[] cachedElementNodeArray, + @Cached("createCallNode(cachedTarget, cachedElementNodeArray, moreArgs)") RCallNode callNode) { + + int dotsLength = dots.getLength(); + int[] lengths = new int[dotsLength]; + int maxLength = -1; + for (int i = 0; i < dotsLength; i++) { + int length = cachedElementNodeArray[i].lengthNode.executeInt(frame, dots.getDataAt(i)); + if (length > maxLength) { + maxLength = length; + } + lengths[i] = length; + } + Object[] result = new Object[maxLength]; + for (int i = 0; i < maxLength; i++) { + /* Evaluate and store the arguments */ + for (int listIndex = 0; listIndex < dotsLength; listIndex++) { + RAbstractContainer vec = (RAbstractContainer) dots.getDataAt(listIndex); + int adjIndex = i % lengths[listIndex]; + RArgsValuesAndNames indexArg; + if (adjIndex < INDEX_CACHE.length) { + indexArg = INDEX_CACHE[adjIndex]; + } else { + indexArg = new RArgsValuesAndNames(new Object[]{adjIndex + 1}, I_INDEX); + } + Object vecElement = cachedElementNodeArray[listIndex].indexedLoadNode.execute(frame, vec, indexArg, EXACT, DROP); + cachedElementNodeArray[listIndex].writeVectorElementNode.execute(frame, vecElement); + } + /* Now call the function */ + result[i] = callNode.execute(frame, function); + } + return result; + } + + @SuppressWarnings("unused") + @Specialization(contains = "cachedMApply") + protected Object[] genericLApply(Object vector, RFunction function, RArgsValuesAndNames additionalArguments) { + throw RError.nyi(getSourceSection(), "generic mApply"); + } + + /** + * Creates the {@link RCallNode} for this target. + * + * TODO names and moreArgs + * + */ + protected RCallNode createCallNode(RootCallTarget callTarget, ElementNode[] elementNodeArray, @SuppressWarnings("unused") RList moreArgs) { + @SuppressWarnings("unused") + FormalArguments formalArgs = ((RRootNode) callTarget.getRootNode()).getFormalArguments(); + + ReadVariableNode[] readVectorElementNodes = new ReadVariableNode[elementNodeArray.length]; + for (int i = 0; i < readVectorElementNodes.length; i++) { + readVectorElementNodes[i] = ReadVariableNode.create(elementNodeArray[i].vectorElementName, false); + } + CallArgumentsNode argsNode = CallArgumentsNode.create(false, false, readVectorElementNodes, ArgumentsSignature.empty(readVectorElementNodes.length)); + return RCallNode.createCall(null, null, argsNode, null); + } + + protected ElementNode[] createElementNodeArray(int length) { + ElementNode[] elementNodes = new ElementNode[length]; + for (int i = 0; i < length; i++) { + elementNodes[i] = insert(new ElementNode(VECTOR_ELEMENT_PREFIX + (i + 1))); + } + return elementNodes; + } + } + +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index cc9bab4b2ebd28ab9e5ebf5b4e037c9a54c99a4d..29ca14194082742d01518461ffb3adf9ba57348d 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -26353,6 +26353,58 @@ character(0) #argv <- list(character(0), '.'); .Internal(make.unique(argv[[1]], argv[[2]])) character(0) +##com.oracle.truffle.r.test.builtins.TestBuiltin_mapply.testmapply +#mapply(function(x, y) seq_len(x) + y, c(a = 1, b = 2, c = 3), c(A = 10, B = 0, C = -10)) +$a +[1] 11 + +$b +[1] 1 2 + +$c +[1] -9 -8 -7 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_mapply.testmapply +#mapply(rep, 1:4, 4:1) +[[1]] +[1] 1 1 1 1 + +[[2]] +[1] 2 2 2 + +[[3]] +[1] 3 3 + +[[4]] +[1] 4 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_mapply.testmapply +#mapply(rep, times = 1:4, MoreArgs = list(x = 42)) +[[1]] +[1] 42 + +[[2]] +[1] 42 42 + +[[3]] +[1] 42 42 42 + +[[4]] +[1] 42 42 42 42 + + +##com.oracle.truffle.r.test.builtins.TestBuiltin_mapply.testmapply +#word <- function(C, k) paste(rep.int(C, k), collapse = ""); utils::str(mapply(word, LETTERS[1:6], 6:1, SIMPLIFY = FALSE)) +List of 6 + $ A: chr "AAAAAA" + $ B: chr "BBBBB" + $ C: chr "CCCC" + $ D: chr "DDD" + $ E: chr "EE" + $ F: chr "F" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_mapply.testmapply1 #argv <- list(.Primitive('c'), list(list(), list(), list()), NULL); .Internal(mapply(argv[[1]], argv[[2]], argv[[3]])) list() diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_mapply.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_mapply.java index dc9fe1d106ed218fe233227a106456b60bd56e58..9322ed77c87c16dbf8a8265c4b4915fde3a8fecc 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_mapply.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_mapply.java @@ -19,6 +19,14 @@ public class TestBuiltin_mapply extends TestBase { @Test public void testmapply1() { - assertEval(Ignored.Unknown, "argv <- list(.Primitive('c'), list(list(), list(), list()), NULL); .Internal(mapply(argv[[1]], argv[[2]], argv[[3]]))"); + assertEval("argv <- list(.Primitive('c'), list(list(), list(), list()), NULL); .Internal(mapply(argv[[1]], argv[[2]], argv[[3]]))"); + } + + @Test + public void testmapply() { + assertEval("mapply(rep, 1:4, 4:1)"); + assertEval("mapply(function(x, y) seq_len(x) + y, c(a = 1, b = 2, c = 3), c(A = 10, B = 0, C = -10))"); + assertEval(Ignored.Unimplemented, "mapply(rep, times = 1:4, MoreArgs = list(x = 42))"); + assertEval(Ignored.Unimplemented, "word <- function(C, k) paste(rep.int(C, k), collapse = \"\"); utils::str(mapply(word, LETTERS[1:6], 6:1, SIMPLIFY = FALSE))"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java index 67b6c651ae858311d125aa54ab9fdf59adb04791..8d68f81b8da76100083ca92870bb53b9161d4fac 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/rpackages/TestRPackages.java @@ -66,17 +66,13 @@ public class TestRPackages extends TestBase { private boolean installPackage(String packageName) { Path packagePath = rpackagesDists.resolve(packageName).resolve("lib").resolve(packageName + ".tar"); - String[] cmds; + String[] cmds = new String[4]; if (generatingExpected()) { // use GnuR - cmds = new String[4]; cmds[0] = "R"; } else { // use FastR - cmds = new String[5]; cmds[0] = FileSystems.getDefault().getPath(REnvVars.rHome(), "bin", "R").toString(); - // TODO remove --no-help limitation when markdown parser functioning - cmds[3] = "--no-help"; } cmds[1] = "CMD"; cmds[2] = "INSTALL";