diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java index 9395c6682ce00d2918381fb53a963329567b0bf6..f269495c60854adbcf3695aa66ae4d1c7fa398e0 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Range.java @@ -65,8 +65,8 @@ public abstract class Range extends RBuiltinNode.Arg3 { @Specialization(guards = "args.getLength() == 1") protected RVector<?> rangeLengthOne(RArgsValuesAndNames args, boolean naRm, boolean finite) { - Object min = minReduce.executeReduce(args.getArgument(0), naRm, finite); - Object max = maxReduce.executeReduce(args.getArgument(0), naRm, finite); + Object min = minReduce.executeReduce(args.getArgument(0), naRm || finite, finite); + Object max = maxReduce.executeReduce(args.getArgument(0), naRm || finite, finite); return createResult(min, max); } @@ -84,8 +84,8 @@ public abstract class Range extends RBuiltinNode.Arg3 { protected RVector<?> range(RArgsValuesAndNames args, boolean naRm, boolean finite, @Cached("create()") Combine combine) { Object combined = combine.executeCombine(args, false); - Object min = minReduce.executeReduce(combined, naRm, finite); - Object max = maxReduce.executeReduce(combined, naRm, finite); + Object min = minReduce.executeReduce(combined, naRm || finite, finite); + Object max = maxReduce.executeReduce(combined, naRm || finite, finite); return createResult(min, max); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java index 4f2f375192eb681092cf8f87eef33ad04c367e80..7230d7b17e60bd571680b3792d0dc235d8562e0c 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryArithmeticReduceNode.java @@ -22,17 +22,17 @@ */ package com.oracle.truffle.r.nodes.unary; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; -import com.oracle.truffle.r.nodes.control.RLengthNode; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.data.RComplex; import com.oracle.truffle.r.runtime.data.RComplexVector; @@ -42,8 +42,8 @@ import com.oracle.truffle.r.runtime.data.RTypes; 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.RAbstractLogicalVector; -import com.oracle.truffle.r.runtime.data.nodes.EnableNACheckNode; -import com.oracle.truffle.r.runtime.data.nodes.VectorIterator; +import com.oracle.truffle.r.runtime.data.model.RAbstractVector; +import com.oracle.truffle.r.runtime.data.nodes.VectorAccess; import com.oracle.truffle.r.runtime.interop.ForeignArray2R; import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.ops.BinaryArithmetic; @@ -57,6 +57,8 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck; * only applies to some types (e.g. double or integer), for other types 'finite' seems to be ignored * (e.g. logical). The only situation where semantics of finite is different to na.rm is double * values: na.rm removes NA and NaN, but not -/+Inf. + * + * FastR handles finite consistently (setting na.rm = TRUE if finite = TRUE) in the range builtin. */ @ImportStatic({RRuntime.class}) @TypeSystemReference(RTypes.class) @@ -64,7 +66,6 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { public abstract Object executeReduce(Object value, boolean naRm, boolean finite); - @Child private MultiElemStringHandlerNode stringHandler; @Child private BinaryArithmetic arithmetic; private final BinaryArithmeticFactory factory; @@ -74,6 +75,8 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { private final NACheck na = NACheck.create(); private final ConditionProfile naRmProfile = ConditionProfile.createBinaryProfile(); + private final BranchProfile emptyProfile = BranchProfile.create(); + private final BranchProfile naResultProfile = BranchProfile.create(); protected UnaryArithmeticReduceNode(ReduceSemantics semantics, BinaryArithmeticFactory factory) { this.factory = factory; @@ -83,21 +86,15 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { this.supportComplex = semantics.supportComplex; } - private String handleString(RStringVector operand, boolean naRm, boolean finite, int offset) { - if (stringHandler == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - stringHandler = insert(new MultiElemStringHandlerNode(semantics, factory, na)); - } - return stringHandler.executeString(operand, naRm, finite, offset); - } - private void emptyWarning() { + emptyProfile.enter(); if (semantics.getEmptyWarning() != null) { warning(semantics.emptyWarning); } } private void naResultWarning() { + naResultProfile.enter(); if (semantics.getNAResultWarning() != null) { warning(semantics.getNAResultWarning()); } @@ -136,20 +133,23 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { protected double doDouble(double operand, boolean naRm, boolean finite, @Cached("createBinaryProfile()") ConditionProfile finiteProfile, @Cached("createBinaryProfile()") ConditionProfile isInfiniteProfile) { + if (finiteProfile.profile(finite) && isInfiniteProfile.profile(!RRuntime.isFinite(operand))) { + emptyWarning(); + return semantics.getIntStart(); + } na.enable(operand); - if (naRmProfile.profile(naRm || finite)) { - boolean profiledFinite = finiteProfile.profile(finite); - if (na.checkNAorNaN(operand) || (profiledFinite && isInfiniteProfile.profile(!RRuntime.isFinite(operand)))) { + if (naRmProfile.profile(naRm)) { + if (na.checkNAorNaN(operand)) { // the only value we have should be removed... emptyWarning(); return semantics.getIntStart(); } else { + // known not to be NA or NaN return operand; } - } else { - // since !naRm and !finite, NaN or +/-Inf can be valid results - return na.check(operand) ? RRuntime.DOUBLE_NA : operand; } + // since !naRm and !finite, NaN or +/-Inf can be valid results + return na.check(operand) ? RRuntime.DOUBLE_NA : operand; } @Specialization @@ -199,35 +199,47 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { } } - @Specialization - protected Object doIntVector(RAbstractIntVector operand, boolean naRm, boolean finite, - @Cached("create()") EnableNACheckNode enableNACheckNode, - @Cached("create()") RLengthNode lengthNode, - @Cached("create()") VectorIterator.Int iterator) { - RBaseNode.reportWork(this, lengthNode.executeInteger(operand)); - boolean profiledNaRm = naRmProfile.profile(naRm || finite); + private Object doInt(RAbstractVector vector, boolean naRm, VectorAccess access) { + boolean profiledNaRm = naRmProfile.profile(naRm); int result = semantics.getIntStart(); - enableNACheckNode.execute(na, operand); - int opCount = 0; - Object it = iterator.init(operand); - while (iterator.hasNext(operand, it)) { - int d = iterator.next(operand, it); - if (na.check(d)) { - if (profiledNaRm) { - continue; - } else { - return RRuntime.INT_NA; + boolean empty = true; + try (VectorAccess.SequentialIterator iter = access.access(vector)) { + while (access.next(iter)) { + int d; + switch (access.getType()) { + case Integer: + d = access.getInt(iter); + if (access.na.check(d)) { + if (profiledNaRm) { + continue; + } else { + return RRuntime.INT_NA; + } + } + break; + case Logical: + byte logical = access.getLogical(iter); + if (access.na.check(logical)) { + if (profiledNaRm) { + continue; + } else { + return RRuntime.INT_NA; + } + } + d = logical; // 0 or 1 + break; + default: + throw RInternalError.shouldNotReachHere(); } - } else { result = arithmetic.op(result, d); if (RRuntime.isNA(result)) { naResultWarning(); return RRuntime.INT_NA; } + empty = false; } - opCount++; } - if (opCount == 0) { + if (empty) { emptyWarning(); if (semantics.isUseDoubleStartForEmptyVector()) { return semantics.getDoubleStart(); @@ -236,88 +248,79 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { return result; } - @Specialization - protected double doDoubleVector(RAbstractDoubleVector operand, boolean naRm, boolean finite, - @Cached("create()") EnableNACheckNode enableNACheckNode, - @Cached("create()") RLengthNode lengthNode, - @Cached("create()") VectorIterator.Double iterator, - @Cached("createBinaryProfile()") ConditionProfile finiteProfile, - @Cached("createBinaryProfile()") ConditionProfile isInfiniteProfile) { - RBaseNode.reportWork(this, lengthNode.executeInteger(operand)); - boolean profiledNaRm = naRmProfile.profile(naRm || finite); - boolean profiledFinite = finiteProfile.profile(finite); - double result = semantics.getDoubleStart(); - enableNACheckNode.execute(na, operand); - int opCount = 0; + @Specialization(guards = "access.supports(vector)") + protected Object doIntCached(RAbstractIntVector vector, boolean naRm, @SuppressWarnings("unused") boolean finite, + @Cached("vector.access()") VectorAccess access) { + return doInt(vector, naRm, access); + } - Object it = iterator.init(operand); - while (iterator.hasNext(operand, it)) { - double d = iterator.next(operand, it); - if (na.checkNAorNaN(d)) { - if (profiledNaRm) { - continue; // ignore NA/NaN - } else if (na.check(d)) { - // NA produces NA directly, but NaN should be handled by arithmetics.op to - // produce NaN. We cannot directly return NaN because if we encounter NA later - // on, we should return NA not NaN - return RRuntime.DOUBLE_NA; - } - } else if (profiledFinite && isInfiniteProfile.profile(!RRuntime.isFinite(d))) { - // ignore -/+Inf if 'infinite == TRUE' - continue; - } + @Specialization(replaces = "doIntCached") + protected Object doIntGeneric(RAbstractIntVector vector, boolean naRm, @SuppressWarnings("unused") boolean finite) { + return doInt(vector, naRm, vector.slowPathAccess()); + } - result = arithmetic.op(result, d); - opCount++; - } - if (opCount == 0) { - emptyWarning(); - } - return result; + @Specialization(guards = "access.supports(vector)") + protected Object doLogicalCached(RAbstractLogicalVector vector, boolean naRm, @SuppressWarnings("unused") boolean finite, + @Cached("vector.access()") VectorAccess access) { + return doInt(vector, naRm, access); } - @Specialization - protected Object doLogicalVector(RAbstractLogicalVector operand, boolean naRm, @SuppressWarnings("unused") boolean finite, - @Cached("create()") EnableNACheckNode enableNACheckNode, - @Cached("create()") RLengthNode lengthNode, - @Cached("create()") VectorIterator.Logical iterator) { - RBaseNode.reportWork(this, lengthNode.executeInteger(operand)); - boolean profiledNaRm = naRmProfile.profile(naRm); - int result = semantics.getIntStart(); - enableNACheckNode.execute(na, operand); - int opCount = 0; + @Specialization(replaces = "doIntCached") + protected Object doLogicalGeneric(RAbstractLogicalVector vector, boolean naRm, @SuppressWarnings("unused") boolean finite) { + return doInt(vector, naRm, vector.slowPathAccess()); + } - Object it = iterator.init(operand); - while (iterator.hasNext(operand, it)) { - byte d = iterator.next(operand, it); - if (na.check(d)) { - if (profiledNaRm) { + private double doDouble(RAbstractDoubleVector vector, boolean naRm, boolean finite, ConditionProfile finiteProfile, ConditionProfile isInfiniteProfile, VectorAccess access) { + boolean profiledNaRm = naRmProfile.profile(naRm); + boolean profiledFinite = finiteProfile.profile(finite); + double result = semantics.getDoubleStart(); + boolean empty = true; + try (VectorAccess.SequentialIterator iter = access.access(vector)) { + while (access.next(iter)) { + double d = access.getDouble(iter); + if (access.na.checkNAorNaN(d)) { + if (profiledNaRm) { + continue; // ignore NA/NaN + } else if (access.na.check(d)) { + // NA produces NA directly, but NaN should be handled by arithmetics.op to + // produce NaN. We cannot directly return NaN because if we encounter NA + // later + // on, we should return NA not NaN + return RRuntime.DOUBLE_NA; + } + } else if (profiledFinite && isInfiniteProfile.profile(!RRuntime.isFinite(d))) { + // ignore -/+Inf if 'infinite == TRUE' continue; - } else { - return RRuntime.INT_NA; } - } else { result = arithmetic.op(result, d); - if (RRuntime.isNA(result)) { - naResultWarning(); - return RRuntime.INT_NA; - } + empty = false; } - opCount++; } - if (opCount == 0) { + if (empty) { emptyWarning(); - if (semantics.isUseDoubleStartForEmptyVector()) { - return semantics.getDoubleStart(); - } } return result; } + @Specialization(guards = "access.supports(vector)") + protected double doDoubleCached(RAbstractDoubleVector vector, boolean naRm, boolean finite, + @Cached("createBinaryProfile()") ConditionProfile finiteProfile, + @Cached("createBinaryProfile()") ConditionProfile isInfiniteProfile, + @Cached("vector.access()") VectorAccess access) { + return doDouble(vector, naRm, finite, finiteProfile, isInfiniteProfile, access); + } + + @Specialization(replaces = "doDoubleCached") + protected double doDoubleGeneric(RAbstractDoubleVector vector, boolean naRm, boolean finite, + @Cached("createBinaryProfile()") ConditionProfile finiteProfile, + @Cached("createBinaryProfile()") ConditionProfile isInfiniteProfile) { + return doDouble(vector, naRm, finite, finiteProfile, isInfiniteProfile, vector.slowPathAccess()); + } + @Specialization(guards = "supportComplex") - protected RComplex doComplexVector(RComplexVector operand, boolean naRm, boolean finite) { + protected RComplex doComplexVector(RComplexVector operand, boolean naRm, @SuppressWarnings("unused") boolean finite) { RBaseNode.reportWork(this, operand.getLength()); - boolean profiledNaRm = naRmProfile.profile(naRm || finite); + boolean profiledNaRm = naRmProfile.profile(naRm); RComplex result = RRuntime.double2complex(semantics.getDoubleStart()); int opCount = 0; na.enable(operand); @@ -344,18 +347,14 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { // does not work for String-s as, in particular, we cannot supply the (lexicographically) // "largest" String for the implementation of max function - private static String doStringVectorEmptyInternal(ReduceSemantics semantics, RBaseNode invokingNode) { + @Specialization(guards = {"supportString", "operand.getLength() == 0"}) + protected String doStringVectorEmpty(@SuppressWarnings("unused") RStringVector operand, @SuppressWarnings("unused") boolean naRm, @SuppressWarnings("unused") boolean finite) { if (semantics.getEmptyWarning() != null) { - RError.warning(invokingNode, semantics.emptyWarningCharacter); + warning(semantics.emptyWarningCharacter); } return semantics.getStringStart(); } - @Specialization(guards = {"supportString", "operand.getLength() == 0"}) - protected String doStringVectorEmpty(@SuppressWarnings("unused") RStringVector operand, @SuppressWarnings("unused") boolean naRm, @SuppressWarnings("unused") boolean finite) { - return doStringVectorEmptyInternal(semantics, this); - } - @Specialization(guards = {"supportString", "operand.getLength() == 1"}) protected String doStringVectorOneElem(RStringVector operand, boolean naRm, boolean finite) { boolean profiledNaRm = naRmProfile.profile(naRm); @@ -371,7 +370,40 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { @Specialization(guards = {"supportString", "operand.getLength() > 1"}) protected String doStringVector(RStringVector operand, boolean naRm, boolean finite) { - return handleString(operand, naRm, finite, 0); + boolean profiledNaRm = naRmProfile.profile(naRm); + na.enable(operand); + int offset = 0; + String result = operand.getDataAt(offset); + if (profiledNaRm) { + while (na.check(result)) { + // the following is meant to eliminate leading NA-s + if (offset == operand.getLength() - 1) { + // last element - all other are NAs + return doStringVectorEmpty(operand, naRm, finite); + } + result = operand.getDataAt(++offset); + } + } else { + if (na.check(result)) { + return result; + } + } + // when we reach here, it means that we have already seen one non-NA element + assert !RRuntime.isNA(result); + for (int i = offset + 1; i < operand.getLength(); i++) { + String current = operand.getDataAt(i); + if (na.check(current)) { + if (profiledNaRm) { + // skip NA-s + continue; + } else { + return RRuntime.STRING_NA; + } + } else { + result = arithmetic.op(result, current); + } + } + return result; } @Specialization(guards = {"isForeignObject(obj)"}) @@ -452,67 +484,4 @@ public abstract class UnaryArithmeticReduceNode extends RBaseNode { return useDoubleStartForEmptyVector; } } - - private static final class MultiElemStringHandlerNode extends RBaseNode { - - @Child private MultiElemStringHandlerNode recursiveStringHandler; - @Child private BinaryArithmetic arithmetic; - - private final ReduceSemantics semantics; - private final BinaryArithmeticFactory factory; - private final NACheck na; - private final ConditionProfile naRmProfile = ConditionProfile.createBinaryProfile(); - - MultiElemStringHandlerNode(ReduceSemantics semantics, BinaryArithmeticFactory factory, NACheck na) { - this.semantics = semantics; - this.factory = factory; - this.arithmetic = factory.createOperation(); - this.na = na; - } - - private String handleString(RStringVector operand, boolean naRm, boolean finite, int offset) { - if (recursiveStringHandler == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - recursiveStringHandler = insert(new MultiElemStringHandlerNode(semantics, factory, na)); - } - return recursiveStringHandler.executeString(operand, naRm, finite, offset); - } - - public String executeString(RStringVector operand, boolean naRm, boolean finite, int offset) { - boolean profiledNaRm = naRmProfile.profile(naRm); - na.enable(operand); - String result = operand.getDataAt(offset); - if (profiledNaRm) { - if (na.check(result)) { - // the following is meant to eliminate leading NA-s - if (offset == operand.getLength() - 1) { - // last element - all other are NAs - return doStringVectorEmptyInternal(semantics, this); - } else { - return handleString(operand, naRm, finite, offset + 1); - } - } - } else { - if (na.check(result)) { - return result; - } - } - // when we reach here, it means that we have already seen one non-NA element - assert !RRuntime.isNA(result); - for (int i = offset + 1; i < operand.getLength(); i++) { - String current = operand.getDataAt(i); - if (na.check(current)) { - if (profiledNaRm) { - // skip NA-s - continue; - } else { - return RRuntime.STRING_NA; - } - } else { - result = arithmetic.op(result, current); - } - } - return result; - } - } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java index acd72feaf1484c0c8e4a36165d7538c523617628..1ee0499aa4ace76f7fa3e2b83eadb38ac6d32d38 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/UnaryNotNode.java @@ -32,34 +32,37 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode; +import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode; +import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.builtins.RBuiltin; -import com.oracle.truffle.r.runtime.data.RComplex; import com.oracle.truffle.r.runtime.data.RDataFactory; -import com.oracle.truffle.r.runtime.data.RList; -import com.oracle.truffle.r.runtime.data.RLogicalVector; +import com.oracle.truffle.r.runtime.data.RDataFactory.VectorFactory; import com.oracle.truffle.r.runtime.data.RRaw; -import com.oracle.truffle.r.runtime.data.RRawVector; -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.RAbstractStringVector; +import com.oracle.truffle.r.runtime.data.RVector; +import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; +import com.oracle.truffle.r.runtime.data.nodes.VectorAccess; +import com.oracle.truffle.r.runtime.data.nodes.VectorAccess.SequentialIterator; +import com.oracle.truffle.r.runtime.data.nodes.VectorReuse; import com.oracle.truffle.r.runtime.interop.ForeignArray2R; -import com.oracle.truffle.r.runtime.ops.na.NACheck; import com.oracle.truffle.r.runtime.ops.na.NAProfile; -@ImportStatic({RRuntime.class}) +@ImportStatic({RRuntime.class, ForeignArray2R.class, Message.class, RType.class}) @RBuiltin(name = "!", kind = PRIMITIVE, parameterNames = {""}, dispatch = OPS_GROUP_GENERIC, behavior = PURE_ARITHMETIC) public abstract class UnaryNotNode extends RBuiltinNode.Arg1 { - private final NACheck na = NACheck.create(); private final NAProfile naProfile = NAProfile.create(); - private final ConditionProfile zeroLengthProfile = ConditionProfile.createBinaryProfile(); + + @Child private GetDimAttributeNode getDims = GetDimAttributeNode.create(); + @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create(); + @Child private GetDimNamesAttributeNode getDimNames = GetDimNamesAttributeNode.create(); static { Casts.noCasts(UnaryNotNode.class); @@ -77,10 +80,6 @@ public abstract class UnaryNotNode extends RBuiltinNode.Arg1 { return RRuntime.asLogical(operand == 0); } - private static byte not(RComplex operand) { - return RRuntime.asLogical(operand.getRealPart() == 0 && operand.getImaginaryPart() == 0); - } - private static byte notRaw(RRaw operand) { return notRaw(operand.getValue()); } @@ -109,112 +108,83 @@ public abstract class UnaryNotNode extends RBuiltinNode.Arg1 { return RDataFactory.createRaw(notRaw(operand)); } - @Specialization - protected RLogicalVector doLogicalVector(RLogicalVector vector) { - int length = vector.getLength(); - byte[] result; - if (zeroLengthProfile.profile(length == 0)) { - result = new byte[0]; - } else { - na.enable(vector); - result = new byte[length]; - for (int i = 0; i < length; i++) { - byte value = vector.getDataAt(i); - result[i] = na.check(value) ? RRuntime.LOGICAL_NA : not(value); + @Specialization(guards = {"vectorAccess.supports(vector)", "reuse.supports(vector)"}) + protected RAbstractVector doLogicalVectorCached(RAbstractLogicalVector vector, + @Cached("vector.access()") VectorAccess vectorAccess, + @Cached("createTemporary(vector)") VectorReuse reuse) { + RAbstractVector result = reuse.getResult(vector); + VectorAccess resultAccess = reuse.access(result); + try (SequentialIterator vectorIter = vectorAccess.access(vector); SequentialIterator resultIter = resultAccess.access(result)) { + while (vectorAccess.next(vectorIter) && resultAccess.next(resultIter)) { + byte value = vectorAccess.getLogical(vectorIter); + resultAccess.setLogical(resultIter, vectorAccess.na.check(value) ? RRuntime.LOGICAL_NA : not(value)); } } - RLogicalVector resultVector = RDataFactory.createLogicalVector(result, na.neverSeenNA()); - resultVector.copyAttributesFrom(vector); - return resultVector; - } - - @Specialization - protected RLogicalVector doIntVector(RAbstractIntVector vector) { - int length = vector.getLength(); - byte[] result; - if (zeroLengthProfile.profile(length == 0)) { - result = new byte[0]; - } else { - na.enable(vector); - result = new byte[length]; - for (int i = 0; i < length; i++) { - int value = vector.getDataAt(i); - result[i] = na.check(value) ? RRuntime.LOGICAL_NA : not(value); - } - } - RLogicalVector resultVector = RDataFactory.createLogicalVector(result, na.neverSeenNA()); - copyNamesDimsDimNames(vector, resultVector); - return resultVector; - } - - @Specialization - protected RLogicalVector doDoubleVector(RAbstractDoubleVector vector) { - int length = vector.getLength(); - byte[] result; - if (zeroLengthProfile.profile(length == 0)) { - result = new byte[0]; - } else { - na.enable(vector); - result = new byte[length]; - for (int i = 0; i < length; i++) { - double value = vector.getDataAt(i); - result[i] = na.check(value) ? RRuntime.LOGICAL_NA : not(value); - } - } - RLogicalVector resultVector = RDataFactory.createLogicalVector(result, na.neverSeenNA()); - copyNamesDimsDimNames(vector, resultVector); - return resultVector; - } - - @Specialization - protected RLogicalVector doComplexVector(RAbstractComplexVector vector) { - int length = vector.getLength(); - byte[] result; - if (zeroLengthProfile.profile(length == 0)) { - result = new byte[0]; - } else { - na.enable(vector); - result = new byte[length]; - for (int i = 0; i < length; i++) { - RComplex value = vector.getDataAt(i); - result[i] = na.check(value) ? RRuntime.LOGICAL_NA : not(value); - } - } - RLogicalVector resultVector = RDataFactory.createLogicalVector(result, na.neverSeenNA()); - copyNamesDimsDimNames(vector, resultVector); - return resultVector; + result.setComplete(vectorAccess.na.neverSeenNA()); + return result; } + @Specialization(replaces = "doLogicalVectorCached") @TruffleBoundary - private void copyNamesDimsDimNames(RAbstractVector vector, RLogicalVector resultVector) { - resultVector.copyNamesDimsDimNamesFrom(vector, this); - } - - @Specialization - protected RRawVector doRawVector(RRawVector vector) { - int length = vector.getLength(); - byte[] result; - if (zeroLengthProfile.profile(length == 0)) { - result = new byte[0]; - } else { - result = new byte[length]; - for (int i = 0; i < length; i++) { - result[i] = notRaw(vector.getRawDataAt(i)); + protected RAbstractVector doLogicalGenericGeneric(RAbstractLogicalVector vector, + @Cached("createTemporaryGeneric()") VectorReuse reuse) { + return doLogicalVectorCached(vector, vector.slowPathAccess(), reuse); + } + + @Specialization(guards = {"vectorAccess.supports(vector)", "!isRAbstractLogicalVector(vector)"}) + protected RAbstractVector doVectorCached(RAbstractVector vector, + @Cached("vector.access()") VectorAccess vectorAccess, + @Cached("createNew(Logical)") VectorAccess resultAccess, + @Cached("createNew(Raw)") VectorAccess rawResultAccess, + @Cached("create()") VectorFactory factory) { + try (SequentialIterator vectorIter = vectorAccess.access(vector)) { + int length = vectorAccess.getLength(vectorIter); + RAbstractVector result; + switch (vectorAccess.getType()) { + case Character: + case List: + case Expression: + // special cases: + if (length == 0) { + return factory.createEmptyLogicalVector(); + } else { + throw error(RError.Message.INVALID_ARG_TYPE); + } + case Raw: + result = factory.createRawVector(length); + try (SequentialIterator resultIter = rawResultAccess.access(result)) { + // raw does not produce a logical result, but (255 - value) + while (vectorAccess.next(vectorIter) && rawResultAccess.next(resultIter)) { + rawResultAccess.setRaw(resultIter, notRaw(vectorAccess.getRaw(vectorIter))); + } + } + ((RVector<?>) result).copyAttributesFrom(vector); + break; + default: + result = factory.createLogicalVector(length, false); + try (SequentialIterator resultIter = resultAccess.access(result)) { + while (vectorAccess.next(vectorIter) && resultAccess.next(resultIter)) { + byte value = vectorAccess.getLogical(vectorIter); + resultAccess.setLogical(resultIter, vectorAccess.na.check(value) ? RRuntime.LOGICAL_NA : not(value)); + } + } + if (vectorAccess.getType() == RType.Logical) { + ((RVector<?>) result).copyAttributesFrom(vector); + } else { + factory.reinitializeAttributes((RVector<?>) result, getDims.getDimensions(vector), getNames.getNames(vector), getDimNames.getDimNames(vector)); + } + break; } + result.setComplete(vectorAccess.na.neverSeenNA()); + return result; } - RRawVector resultVector = RDataFactory.createRawVector(result); - resultVector.copyAttributesFrom(vector); - return resultVector; } - @Specialization(guards = {"vector.getLength() == 0"}) - protected RLogicalVector doStringVector(@SuppressWarnings("unused") RAbstractStringVector vector) { - return RDataFactory.createEmptyLogicalVector(); - } - - @Specialization(guards = {"list.getLength() == 0"}) - protected RLogicalVector doList(@SuppressWarnings("unused") RList list) { - return RDataFactory.createEmptyLogicalVector(); + @Specialization(replaces = "doVectorCached", guards = "!isRAbstractLogicalVector(vector)") + @TruffleBoundary + protected RAbstractVector doGenericGeneric(RAbstractVector vector, + @Cached("create()") VectorFactory factory) { + return doVectorCached(vector, vector.slowPathAccess(), VectorAccess.createSlowPathNew(RType.Logical), VectorAccess.createSlowPathNew(RType.Raw), factory); } @Specialization(guards = {"isForeignObject(obj)"})