diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/RMultinom.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/RMultinom.java index 8480bde3c0db7647e925bf2254fff2c68d00e5e9..079a0ac5aaa636b00b4fffcc9f0bd6f60947766f 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/RMultinom.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/RMultinom.java @@ -29,10 +29,10 @@ import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.library.stats.RandGenerationFunctions.RandomNumberProvider; import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode; import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode; -import com.oracle.truffle.r.nodes.attributes.UpdateSharedAttributeNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode; +import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; @@ -58,7 +58,7 @@ public abstract class RMultinom extends RExternalBuiltinNode.Arg3 { @Cached("create()") ReuseNonSharedNode reuseNonSharedNode, @Cached("createClassProfile()") ValueProfile randGeneratorClassProfile, @Cached("createBinaryProfile()") ConditionProfile hasAttributesProfile, - @Cached("create()") UpdateSharedAttributeNode updateSharedAttributeNode, + @Cached("create()") UpdateShareableChildValueNode updateSharedAttributeNode, @Cached("createNames()") GetFixedAttributeNode getNamesNode, @Cached("createDimNames()") SetFixedAttributeNode setDimNamesNode) { RAbstractDoubleVector nonSharedProbs = (RAbstractDoubleVector) reuseNonSharedNode.execute(probsVec); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java index 10853a996c8aee8f4c672122aeb90f4cee0f4ebd..c961850f0f7c60e77cd608edd7cac570faa7c58c 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java @@ -40,9 +40,9 @@ import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.attributes.GetAttributeNode; import com.oracle.truffle.r.nodes.attributes.IterableAttributeNode; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetRowNamesAttributeNode; -import com.oracle.truffle.r.nodes.attributes.UpdateSharedAttributeNode; import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; @@ -67,7 +67,7 @@ public abstract class Attr extends RBuiltinNode { @CompilationFinal private String cachedName = ""; @CompilationFinal private String cachedInternedName = ""; - @Child private UpdateSharedAttributeNode sharedAttrUpdate = UpdateSharedAttributeNode.create(); + @Child private UpdateShareableChildValueNode sharedAttrUpdate = UpdateShareableChildValueNode.create(); @Child private GetAttributeNode attrAccess = GetAttributeNode.create(); @Child private IterableAttributeNode iterAttrAccess = IterableAttributeNode.create(); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java index 7d5a0921655097e82255396f22c19b04341c69db..5a6b09782203e748ec27868e9d68f7dd2a4c14d6 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/ExtractListElement.java @@ -26,19 +26,16 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.ConditionProfile; -import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; -import com.oracle.truffle.r.nodes.access.vector.ExtractListElementNodeGen.UpdateStateOfListElementNodeGen; +import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode; import com.oracle.truffle.r.runtime.data.RListBase; -import com.oracle.truffle.r.runtime.data.RShareable; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; import com.oracle.truffle.r.runtime.data.model.RAbstractListVector; -import com.oracle.truffle.r.runtime.data.model.RAbstractVector; /** * Internal node that extracts data under given index from any RAbstractContainer. In the case of - * RListBase, it also invokes {@link UpdateStateOfListElement} on the element before returning it. + * RListBase, it also invokes {@link UpdateShareableChildValueNode} on the element before returning + * it. * * There are two reasons for why one accesses an element of a list: to peek at it, possibly * calculate some values from it, and then forget it. In such case, it is OK to access the element @@ -46,9 +43,9 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector; * {@link RAbstractContainer#getDataAtAsObject(int)}. However, if the object is going to be returned * to the user either as return value of a built-in, put inside a list, put as an attribute, or its * true reference count matters for some other reason, then its reference count must be put into a - * consistent state, which is done by {@link UpdateStateOfListElement}. This node is a convenient - * wrapper that performs the extraction as well as invocation of {@link UpdateStateOfListElement}. - * See also the documentation of {@link RListBase}. + * consistent state, which is done by {@link UpdateShareableChildValueNode}. This node is a + * convenient wrapper that performs the extraction as well as invocation of + * {@link UpdateShareableChildValueNode}. See also the documentation of {@link RListBase}. */ @TypeSystemReference(EmptyTypeSystemFlatLayout.class) public abstract class ExtractListElement extends Node { @@ -60,7 +57,7 @@ public abstract class ExtractListElement extends Node { } @Specialization - protected Object doList(RListBase list, int index, @Cached("create()") UpdateStateOfListElement updateStateNode) { + protected Object doList(RListBase list, int index, @Cached("create()") UpdateShareableChildValueNode updateStateNode) { Object element = list.getDataAt(index); return updateStateNode.updateState(list, element); } @@ -73,61 +70,4 @@ public abstract class ExtractListElement extends Node { protected static boolean isNotList(RAbstractContainer x) { return !(x instanceof RAbstractListVector); } - - @TypeSystemReference(EmptyTypeSystemFlatLayout.class) - public abstract static class UpdateStateOfListElement extends Node { - - public abstract void execute(Object owner, Object item); - - /** - * Provides more convenient interface for the {@link #execute(Object, Object)} method. - */ - public final <T> T updateState(RAbstractContainer owner, T item) { - execute(owner, item); - return item; - } - - public static UpdateStateOfListElement create() { - return UpdateStateOfListElementNodeGen.create(); - } - - @Specialization - protected void doShareableValues(RListBase owner, RShareable value, - @Cached("createClassProfile()") ValueProfile valueProfile, - @Cached("createBinaryProfile()") ConditionProfile sharedValue, - @Cached("createBinaryProfile()") ConditionProfile temporaryOwner) { - RShareable profiledValue = valueProfile.profile(value); - if (sharedValue.profile(profiledValue.isShared())) { - // it is already shared, not need to do anything - return; - } - - if (temporaryOwner.profile(owner.isTemporary())) { - // This can happen, for example, when we immediately extract out of a temporary - // list that was returned by a built-in, like: strsplit(...)[[1L]]. We do not need - // to transition the element, it may stay temporary. - return; - } - - if (profiledValue.isTemporary()) { - // make it at least non-shared (parent list must be also at least non-shared) - profiledValue.incRefCount(); - } - if (owner.isShared()) { - // owner is shared, make the value shared too - profiledValue.incRefCount(); - } - } - - @Specialization(guards = "isFallback(owner, value)") - protected void doFallback(Object owner, Object value) { - assert !(value instanceof RShareable && owner instanceof RAbstractVector && !(owner instanceof RListBase)) : "RShareables can only live inside lists and no other vectors."; - // nop: either value is not RShareable, or the owner is "list" like structure with - // reference semantics (e.g. REnvironment) - } - - protected static boolean isFallback(Object owner, Object value) { - return !(value instanceof RShareable) || !(owner instanceof RListBase); - } - } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java index b96380cab8e9f6aa985faee814e5818c88a57986..4b3a19907c7d4ba592ee63ebe20d076e1632b40f 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/WriteIndexedVectorNode.java @@ -32,8 +32,8 @@ import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.LoopConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; -import com.oracle.truffle.r.nodes.access.vector.ExtractListElement.UpdateStateOfListElement; import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; +import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode; import com.oracle.truffle.r.nodes.profile.AlwaysOnBranchProfile; import com.oracle.truffle.r.nodes.profile.IntValueProfile; import com.oracle.truffle.r.nodes.profile.VectorLengthProfile; @@ -473,14 +473,14 @@ abstract class WriteIndexedVectorNode extends Node { private final boolean setListElementAsObject; private final boolean isReplace; - @Child private UpdateStateOfListElement updateStateOfListElement; + @Child private UpdateShareableChildValueNode updateStateOfListElement; @Child private ShareObjectNode shareObjectNode; WriteListAction(boolean setListElementAsObject, boolean isReplace) { this.setListElementAsObject = setListElementAsObject; this.isReplace = isReplace; if (!isReplace) { - updateStateOfListElement = UpdateStateOfListElement.create(); + updateStateOfListElement = UpdateShareableChildValueNode.create(); } else { shareObjectNode = ShareObjectNode.create(); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/ArrayAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/ArrayAttributeNode.java index 7671b2971d04890eee553ae87d73d8522131d786..cfe144c94ac383973b0dd0babfa49227070b4415 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/ArrayAttributeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/ArrayAttributeNode.java @@ -24,23 +24,33 @@ package com.oracle.truffle.r.nodes.attributes; import java.util.List; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.object.Property; import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.r.runtime.data.RAttributable; +import com.oracle.truffle.r.runtime.data.RAttributeStorage; import com.oracle.truffle.r.runtime.data.RAttributesLayout; import com.oracle.truffle.r.runtime.data.RAttributesLayout.AttrsLayout; import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute; public abstract class ArrayAttributeNode extends AttributeIterativeAccessNode { + private static final RAttribute[] EMPTY = new RAttribute[0]; + + @Child private ArrayAttributeNode recursive; + public static ArrayAttributeNode create() { return ArrayAttributeNodeGen.create(); } - public abstract RAttribute[] execute(DynamicObject attrs); + public abstract RAttribute[] execute(Object attrs); @Specialization(limit = "CACHE_LIMIT", guards = {"attrsLayout != null", "attrsLayout.shape.check(attrs)"}) @ExplodeLoop @@ -69,7 +79,29 @@ public abstract class ArrayAttributeNode extends AttributeIterativeAccessNode { } return result; - } + @Specialization + protected RAttribute[] getArrayFallback(RAttributable x, + @Cached("create()") BranchProfile attrNullProfile, + @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, + @Cached("createClassProfile()") ValueProfile xTypeProfile) { + DynamicObject attributes; + if (attrStorageProfile.profile(x instanceof RAttributeStorage)) { + attributes = ((RAttributeStorage) x).getAttributes(); + } else { + attributes = xTypeProfile.profile(x).getAttributes(); + } + + if (attributes == null) { + attrNullProfile.enter(); + return EMPTY; + } + if (recursive == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + recursive = insert(create()); + } + + return recursive.execute(attributes); + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccessNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccessNode.java index d67f5a81d74a009919ed2ad01e5122267b66bc22..5cf678a06dfe84f878b30ad0ca5e27d0b6b35a2e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccessNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeAccessNode.java @@ -23,16 +23,19 @@ package com.oracle.truffle.r.nodes.attributes; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.object.Location; import com.oracle.truffle.api.object.Property; import com.oracle.truffle.api.object.Shape; +import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; import com.oracle.truffle.r.runtime.nodes.RBaseNode; /** * The base class for the nodes that get/set/remove attributes. It encapsulates the common methods * used in guards and for caching. */ +@TypeSystemReference(EmptyTypeSystemFlatLayout.class) public abstract class AttributeAccessNode extends RBaseNode { protected AttributeAccessNode() { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeIterativeAccessNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeIterativeAccessNode.java index a0eee1c48c83079f7a08ce425e143249a7167d50..525c7491fe44c695ec4b011174e7c4b84e092a0e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeIterativeAccessNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/AttributeIterativeAccessNode.java @@ -24,11 +24,13 @@ package com.oracle.truffle.r.nodes.attributes; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.object.Property; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; import com.oracle.truffle.r.runtime.data.RAttributesLayout; import com.oracle.truffle.r.runtime.data.RAttributesLayout.AttrsLayout; import com.oracle.truffle.r.runtime.nodes.RBaseNode; @@ -41,6 +43,7 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode; * properties (i.e. attributes) it is unnecessary to invoke method {@link Shape#getPropertyList()}, * which would be more expensive. */ +@TypeSystemReference(EmptyTypeSystemFlatLayout.class) public abstract class AttributeIterativeAccessNode extends RBaseNode { protected static final int CACHE_LIMIT = RAttributesLayout.LAYOUTS.length; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/IterableAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/IterableAttributeNode.java index 99decfc061953e1c6bc18a7cd0b6d8c317a57870..9032aaefedfbd5b61744a27671cf38dc243df9f7 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/IterableAttributeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/IterableAttributeNode.java @@ -22,20 +22,28 @@ */ package com.oracle.truffle.r.nodes.attributes; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.object.DynamicObject; +import com.oracle.truffle.api.profiles.BranchProfile; +import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.r.runtime.data.RAttributable; +import com.oracle.truffle.r.runtime.data.RAttributeStorage; import com.oracle.truffle.r.runtime.data.RAttributesLayout; import com.oracle.truffle.r.runtime.data.RAttributesLayout.AttrsLayout; public abstract class IterableAttributeNode extends AttributeIterativeAccessNode { + @Child private IterableAttributeNode recursive; + public static IterableAttributeNode create() { return IterableAttributeNodeGen.create(); } - public abstract RAttributesLayout.RAttributeIterable execute(DynamicObject attrs); + public abstract RAttributesLayout.RAttributeIterable execute(Object attr); @Specialization(limit = "CACHE_LIMIT", guards = {"attrsLayout != null", "shapeCheck(attrsLayout.shape, attrs)"}) protected RAttributesLayout.RAttributeIterable getArrayFromConstantLayouts(DynamicObject attrs, @@ -49,4 +57,27 @@ public abstract class IterableAttributeNode extends AttributeIterativeAccessNode return RAttributesLayout.asIterable(attrs); } + @Specialization + protected RAttributesLayout.RAttributeIterable getArrayFallback(RAttributable x, + @Cached("create()") BranchProfile attrNullProfile, + @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, + @Cached("createClassProfile()") ValueProfile xTypeProfile) { + DynamicObject attributes; + if (attrStorageProfile.profile(x instanceof RAttributeStorage)) { + attributes = ((RAttributeStorage) x).getAttributes(); + } else { + attributes = xTypeProfile.profile(x).getAttributes(); + } + + if (attributes == null) { + attrNullProfile.enter(); + return RAttributesLayout.RAttributeIterable.EMPTY; + } + if (recursive == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + recursive = insert(create()); + } + + return recursive.execute(attributes); + } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetAttributeNode.java index aecd9e3c0fc95b1f2f6cdb3c2bb80e3c5ff862da..3b86bb862727a741e13091ce09c2bd026658d5c4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetAttributeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetAttributeNode.java @@ -34,6 +34,7 @@ import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.data.RAttributable; import com.oracle.truffle.r.runtime.data.RAttributeStorage; @@ -148,7 +149,8 @@ public abstract class SetAttributeNode extends AttributeAccessNode { protected void setAttrInAttributable(RAttributable x, String name, Object value, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { DynamicObject attributes; if (attrStorageProfile.profile(x instanceof RAttributeStorage)) { attributes = ((RAttributeStorage) x).getAttributes(); @@ -167,6 +169,11 @@ public abstract class SetAttributeNode extends AttributeAccessNode { } recursive.execute(attributes, name, value); + + // TODO: To verify: It might be beneficial to increment the reference counter only if the + // old and new values differ. One should verify, though, whether the costs brought about by + // reading the old value do not prevail in the end. + updateRefCountNode.execute(value); } /** diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetFixedAttributeNode.java index fd79ede63e6e57041d801f7eecbd7355ca1f50e6..cccbc1cdfc6d8c9e95974478cdbfb17e2cfd7f9c 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetFixedAttributeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetFixedAttributeNode.java @@ -35,6 +35,7 @@ import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetSpecialAttributeNode; +import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.data.RAttributable; import com.oracle.truffle.r.runtime.data.RAttributeStorage; @@ -126,7 +127,8 @@ public abstract class SetFixedAttributeNode extends FixedAttributeAccessNode { protected void setAttrInAttributable(RAttributable x, Object value, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { DynamicObject attributes; if (attrStorageProfile.profile(x instanceof RAttributeStorage)) { attributes = ((RAttributeStorage) x).getAttributes(); @@ -145,6 +147,8 @@ public abstract class SetFixedAttributeNode extends FixedAttributeAccessNode { } recursive.execute(attributes, value); + + updateRefCountNode.execute(value); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java index e596ca4f5b734b8f433cb47a4b912ab49de67e0a..bc0e2c8a3a6f17199f309e76b7b0f31b9606d8da 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java @@ -31,6 +31,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.LoopConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctionsFactory.GetDimAttributeNodeGen; +import com.oracle.truffle.r.nodes.function.opt.ShareObjectNode; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; @@ -318,7 +319,8 @@ public final class SpecialAttributesFunctions { @Cached("create()") SetDimNamesAttributeNode setDimNamesNode, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { RVector<?> xProfiled = xTypeProfile.profile(x); if (newNames.getLength() > xProfiled.getLength()) { namesTooLongProfile.enter(); @@ -342,7 +344,7 @@ public final class SpecialAttributesFunctions { return; } - super.setAttrInAttributable(xProfiled, newNames, attrNullProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(xProfiled, newNames, attrNullProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } } @@ -454,7 +456,8 @@ public final class SpecialAttributesFunctions { protected void setOneDimInVector(RVector<?> x, int dim, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { RAbstractContainer xProfiled = contArgClassProfile.profile(x); int[] dims = new int[]{dim}; @@ -467,17 +470,19 @@ public final class SpecialAttributesFunctions { attrNullProfile.enter(); attrs = RAttributesLayout.createDim(dimVec); xProfiled.initAttributes(attrs); + updateRefCountNode.execute(dimVec); return; } - super.setAttrInAttributable(x, dimVec, attrNullProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(x, dimVec, attrNullProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } @Specialization(insertBefore = "setAttrInAttributable") protected void setDimsInVector(RVector<?> x, RAbstractIntVector dims, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { RAbstractContainer xProfiled = contArgClassProfile.profile(x); verifyDimensions(xProfiled.getLength(), dims); @@ -486,10 +491,11 @@ public final class SpecialAttributesFunctions { attrNullProfile.enter(); attrs = RAttributesLayout.createDim(dims); xProfiled.initAttributes(attrs); + updateRefCountNode.execute(dims); return; } - super.setAttrInAttributable(x, dims, attrNullProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(x, dims, attrNullProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } @Specialization(insertBefore = "setAttrInAttributable") @@ -683,7 +689,8 @@ public final class SpecialAttributesFunctions { @Cached("create()") BranchProfile resizeDimsProfile, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { int[] dimensions = getDimNode.getDimensions(x); if (dimensions == null) { nullDimsProfile.enter(); @@ -727,10 +734,11 @@ public final class SpecialAttributesFunctions { if (x.getAttributes() == null) { attrNullProfile.enter(); x.initAttributes(RAttributesLayout.createDimNames(resDimNames)); + updateRefCountNode.execute(resDimNames); return; } - super.setAttrInAttributable(x, resDimNames, attrNullProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(x, resDimNames, attrNullProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } private static boolean isValidDimLength(RStringVector x, int expectedDim) { @@ -824,13 +832,15 @@ public final class SpecialAttributesFunctions { protected void setRowNamesInVector(RVector<?> x, RAbstractVector newRowNames, @Cached("create()") BranchProfile attrNullProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { if (x.getAttributes() == null) { attrNullProfile.enter(); x.initAttributes(RAttributesLayout.createRowNames(newRowNames)); + updateRefCountNode.execute(newRowNames); return; } - setAttrInAttributable(x, newRowNames, attrNullProfile, attrStorageProfile, xTypeProfile); + setAttrInAttributable(x, newRowNames, attrNullProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } @Specialization(insertBefore = "setAttrInAttributable") @@ -923,8 +933,9 @@ public final class SpecialAttributesFunctions { @Cached("createBinaryProfile()") ConditionProfile nullClassProfile, @Cached("createBinaryProfile()") ConditionProfile notNullClassProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { - handleVector(vector, null, removeClassAttrNode, initAttrProfile, nullAttrProfile, nullClassProfile, notNullClassProfile, attrStorageProfile, xTypeProfile); + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { + handleVector(vector, null, removeClassAttrNode, initAttrProfile, nullAttrProfile, nullClassProfile, notNullClassProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } @Specialization(insertBefore = "setAttrInAttributable") @@ -935,7 +946,8 @@ public final class SpecialAttributesFunctions { @Cached("createBinaryProfile()") ConditionProfile nullClassProfile, @Cached("createBinaryProfile()") ConditionProfile notNullClassProfile, @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile, - @Cached("createClassProfile()") ValueProfile xTypeProfile) { + @Cached("createClassProfile()") ValueProfile xTypeProfile, + @Cached("create()") ShareObjectNode updateRefCountNode) { DynamicObject attrs = vector.getAttributes(); boolean initializeAttrs = initAttrProfile.profile(attrs == null && classAttr != null && classAttr.getLength() != 0); @@ -943,6 +955,7 @@ public final class SpecialAttributesFunctions { nullAttrProfile.enter(); attrs = RAttributesLayout.createClass(classAttr); vector.initAttributes(attrs); + updateRefCountNode.execute(classAttr); } if (nullClassProfile.profile(attrs != null && (classAttr == null || classAttr.getLength() == 0))) { removeAttributeMapping(vector, attrs, removeClassAttrNode); @@ -953,7 +966,7 @@ public final class SpecialAttributesFunctions { // TODO: Isn't this redundant when the same operation is done after the // loop? if (!initializeAttrs) { - super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } // setClassAttrNode.execute(attrs, classAttr); if (vector.getElementClass() != RInteger.class) { @@ -971,7 +984,7 @@ public final class SpecialAttributesFunctions { } if (!initializeAttrs) { - super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile); + super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile, updateRefCountNode); } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/package-info.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..b9490530d1b5e41a61fddeef56b05ea61df7702d --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/package-info.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016, 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. + */ + +/** + * <h2>Attributes handling nodes</h2> Generally, this package contains nodes performing basic + * operations on attributes, such as setting, getting, removing and iterating. To achieve better + * performance, these nodes should be used in preference to the methods on the objects carrying + * attributes. In essence, the <code>execute</code> method of each node accepts as its first + * argument an object carrying attributes, which may be either an instance of + * {@link com.oracle.truffle.api.object.DynamicObject} or + * {@link com.oracle.truffle.r.runtime.data.RAttributable} (i.e. lists, vectors etc.). + * <p> + * <h3>Arbitrary attribute nodes</h3> The nodes in this group operate on the attribute specified as + * the second argument of the <code>execute</code> method. + * <ul> + * <li>{@link com.oracle.truffle.r.nodes.attributes.GetAttributeNode}: retrieves the value of an + * arbitrary attribute + * <li>{@link com.oracle.truffle.r.nodes.attributes.SetAttributeNode}: sets the value of an + * arbitrary attribute. If the first argument is an instance + * {@link com.oracle.truffle.r.runtime.data.RAttributable}, the node initializes the object with the + * empty attributes. + * </ul> + * + * <h3>Fixed attribute nodes</h3> The nodes in this group operate on the attribute that is specified + * during the initialization of a node. + * <ul> + * <li>{@link com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode}: retrieves the value of + * the predefined attribute + * <li>{@link com.oracle.truffle.r.nodes.attributes.HasFixedAttributeNode}: determines the existence + * of the predefined attribute + * <li>{@link com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode}: sets the value of the + * predefined attribute. If the first argument is an instance + * {@link com.oracle.truffle.r.runtime.data.RAttributable}, the node initializes the object with the + * empty attributes. + * <li>{@link com.oracle.truffle.r.nodes.attributes.RemoveFixedAttributeNode}: removes the + * predefined attribute + * </ul> + * There are additional subclasses of the above-mentioned nodes handling the special attributes, + * such as <code>names</code>, <code>dimnames</code> etc. + * + * <h3>Special attributes handling</h3> The nodes handling the special attributes are derived from + * the fixed attribute nodes described in the previous section. The logic in these special attribute + * nodes implements side-effects that take place when a given special attribute is retrieved from, + * set to or removed from an instance of {@link com.oracle.truffle.r.runtime.data.RAttributable}. + * <p> + * N.B. The nodes define additional specializations reflecting the fact that those side-effects are + * polymorphic (i.e. they may depend on the particular class). These specializations implement in a + * more efficient way the logic of their counterparts in attributable objects (such as + * {@link com.oracle.truffle.r.runtime.data.model.RAbstractContainer#setDimNames(com.oracle.truffle.r.runtime.data.RList)} + * ). + * <p> + * The setter nodes are outlined in the following list: + * <ul> + * <li> + * {@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode} + * <li>{@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode} + * <li> + * {@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode} + * <li> + * {@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode} + * <li> + * {@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetRowNamesAttributeNode} + * </ul> + * For each node in the list there is a corresponding "get", "has" and "remove" counterpart. + * <p> + * When creating a fixed attribute node, one needn't take care of whether the attribute is a special + * one or not. The static factory methods defined on the base fixed attribute nodes take care of + * that and create the corresponding instance as long as the attribute is a special one. Thus, all + * the following initializations produce an instance of + * {@link com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode}. + * + * <pre> + * @Child private SetFixedAttributeNode setDimNode = SetFixedAttributeNode.create("dim"); + * @Child private SetFixedAttributeNode setDimNode = SetFixedAttributeNode.create(RRuntime.DIM_ATTR_KEY); + * @Child private SetFixedAttributeNode setDimNode = SetFixedAttributeNode.createDim(); + * @Child private SetFixedAttributeNode setDimNode = SetDimAttributeNode.create(); + * </pre> + * + * Similarly, one does not need to take care of the special attributes when accessing arbitrary + * attributes in an attributable instance. As shown in the following snippet, the arbitrary + * attribute node recognizes a potential special attribute and handles it appropriately. + * <p> + * N.B. This mechanism works for instances of + * {@link com.oracle.truffle.r.runtime.data.RAttributable} only. + * + * <pre> + * @Child + * private SetFixedAttributeNode setAttrNode = SetAttributeNode.create(); + * + * @Specialization + * protected Object handleStringVector(RAbstractStringVector v, String attrName, Object attrValue) { + * ... + * setAttrNode.execute(vector, attrName, attrValue); + * ... + * } + * </pre> + * + * <h3>Iterative nodes</h3> There are two nodes returning iterable instances. The elements returned + * by those objects are instances of + * {@link com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute}. + * <ul> + * <li>{@link com.oracle.truffle.r.nodes.attributes.ArrayAttributeNode} + * <li>{@link com.oracle.truffle.r.nodes.attributes.IterableAttributeNode} + * </ul> + * The above-mentioned nodes always return a non-null instance, even if an attributable instance has + * no attributes. + * + * <pre> + * @Child private IterableAttributeNode iterAttrAccess = IterableAttributeNode.create(); + * + * @Specialization + * protected Object handleStringVector(RAbstractStringVector v) { + * ... + * for (RAttribute a : iterAttrAccess.execute(v)) { + * if ("foo".equals(a.getName())) { + * ... + * } + * } + * ... + * } + * </pre> + * + */ +package com.oracle.truffle.r.nodes.attributes; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java index de2c9eda806469d95e22a5c8c9962e4cafe612a0..81ac78c1819f2a472380e7e554f8a6eb87eae8db 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/ShareObjectNode.java @@ -30,8 +30,10 @@ import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.ConditionProfile; +import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; import com.oracle.truffle.r.runtime.data.RShareable; +import com.oracle.truffle.r.runtime.data.RSharingAttributeStorage; /** * Internal node that should be used whenever you need to increment reference count of some object. @@ -48,7 +50,7 @@ public abstract class ShareObjectNode extends Node { } @Specialization - protected Object doShareable(RShareable obj, + protected Object doShareable(RSharingAttributeStorage obj, @Cached("createBinaryProfile()") ConditionProfile sharedPermanent) { if (sharedPermanent.profile(!obj.isSharedPermanent())) { obj.incRefCount(); @@ -56,6 +58,17 @@ public abstract class ShareObjectNode extends Node { return obj; } + @Specialization + protected Object doShareable(RShareable obj, + @Cached("createBinaryProfile()") ConditionProfile sharedPermanent, + @Cached("createClassProfile()") ValueProfile typeProfile) { + RShareable objProfiled = typeProfile.profile(obj); + if (sharedPermanent.profile(!objProfiled.isSharedPermanent())) { + objProfiled.incRefCount(); + } + return obj; + } + @Specialization(guards = "!isRShareable(obj)") protected Object doNonShareable(Object obj) { return obj; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UpdateSharedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/UpdateShareableChildValueNode.java similarity index 64% rename from com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UpdateSharedAttributeNode.java rename to com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/UpdateShareableChildValueNode.java index 58bbb082347854e069fd6c8b633021fabf357dc5..5a5a903247df6a7bc4e65da814296a21479f43e4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UpdateSharedAttributeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/opt/UpdateShareableChildValueNode.java @@ -20,18 +20,19 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.r.nodes.attributes; +package com.oracle.truffle.r.nodes.function.opt; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.runtime.data.RShareable; +import com.oracle.truffle.r.runtime.data.RSharingAttributeStorage; import com.oracle.truffle.r.runtime.nodes.RBaseNode; -public abstract class UpdateSharedAttributeNode extends RBaseNode { +public abstract class UpdateShareableChildValueNode extends RBaseNode { - protected UpdateSharedAttributeNode() { + protected UpdateShareableChildValueNode() { } public abstract void execute(Object owner, Object attrValue); @@ -41,13 +42,15 @@ public abstract class UpdateSharedAttributeNode extends RBaseNode { return item; } - public static UpdateSharedAttributeNode create() { - return UpdateSharedAttributeNodeGen.create(); + public static UpdateShareableChildValueNode create() { + return UpdateShareableChildValueNodeGen.create(); } @Specialization protected void doShareableValues(RShareable owner, RShareable value, @Cached("createClassProfile()") ValueProfile valueProfile, + @Cached("createBinaryProfile()") ConditionProfile sharingAttrsStorageOwner, + @Cached("createClassProfile()") ValueProfile ownerProfile, @Cached("createBinaryProfile()") ConditionProfile sharedValue, @Cached("createBinaryProfile()") ConditionProfile temporaryOwner) { RShareable profiledValue = valueProfile.profile(value); @@ -56,17 +59,34 @@ public abstract class UpdateSharedAttributeNode extends RBaseNode { return; } + if (sharingAttrsStorageOwner.profile(owner instanceof RSharingAttributeStorage)) { + // monomorphic invocations of the owner + RSharingAttributeStorage shOwner = (RSharingAttributeStorage) owner; + incRef(shOwner, profiledValue, temporaryOwner); + } else { + // invoking a type-profiled owner + RShareable ownerProfiled = ownerProfile.profile(owner); + incRef(ownerProfiled, profiledValue, temporaryOwner); + } + } + + private static void incRef(RShareable owner, RShareable value, ConditionProfile temporaryOwner) { if (temporaryOwner.profile(owner.isTemporary())) { + // This can happen, for example, when we immediately extract out of a temporary + // list that was returned by a built-in, like: strsplit(...)[[1L]]. We do not need + // to transition the element, it may stay temporary. return; } - if (profiledValue.isTemporary()) { + // the owner is at least non-shared + + if (value.isTemporary()) { // make it at least non-shared (the owner must be also at least non-shared) - profiledValue.incRefCount(); + value.incRefCount(); } if (owner.isShared()) { // owner is shared, make the attribute value shared too - profiledValue.incRefCount(); + value.incRefCount(); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributesLayout.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributesLayout.java index 8f35974969087a286e79230a3b7af3788e483ff2..523100966ba101fd359421ba7c15423cbd3684e2 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributesLayout.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributesLayout.java @@ -22,6 +22,7 @@ */ package com.oracle.truffle.r.runtime.data; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -257,6 +258,9 @@ public final class RAttributesLayout { } public static final class RAttributeIterable implements Iterable<RAttributesLayout.RAttribute> { + + public static final RAttributeIterable EMPTY = new RAttributeIterable(null, null); + private final DynamicObject attrs; private final List<Property> properties; @@ -267,7 +271,11 @@ public final class RAttributesLayout { @Override public Iterator<RAttributesLayout.RAttribute> iterator() { - return new Iter(attrs, properties.iterator()); + if (attrs == null || properties == null) { + return Collections.emptyIterator(); + } else { + return new Iter(attrs, properties.iterator()); + } } } 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 b7d3de3e8cfa5e068c4c8de8bfb49016f1be01f6..71f1784301243b7a76d79cad999efb22a031ffe7 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 @@ -8309,6 +8309,16 @@ Error in attr(x, 42) <- NULL : 'name' must be non-null character string #x<-42; attr(x, NULL) <- NULL Error in attr(x, NULL) <- NULL : 'name' must be non-null character string +##com.oracle.truffle.r.test.builtins.TestBuiltin_attrassign.testRefCount# +#x <- c(1,2); attr(x, "foo") <- c("a","b"); y <- x; attr(x,"foo")[[1]] <- "c"; y +[1] 1 2 +attr(,"foo") +[1] "a" "b" + +##com.oracle.truffle.r.test.builtins.TestBuiltin_attrassign.testRefCount# +#x <- c(1,2,3); y <- 42; attr(y, 'at') <- x; x[[1]] <- 2; attr(y, 'at') +[1] 1 2 3 + ##com.oracle.truffle.r.test.builtins.TestBuiltin_attrassign.testattrassign1# #argv <- list(structure(1, foo = structure(list(a = 'a'), .Names = 'a')), 'foo', value = structure(list(a = 'a'), .Names = 'a'));`attr<-`(argv[[1]],argv[[2]],argv[[3]]); [1] 1 @@ -27555,6 +27565,12 @@ $z [1] 42 +##com.oracle.truffle.r.test.builtins.TestBuiltin_list.testRefCount# +#{ l <- list(a=c(1,2)); l2 <- l; l$a[[1]] <- 3; l2 } +$a +[1] 1 2 + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_list.testlist1# #argv <- list(surname = structure(c('McNeil', 'Ripley', 'Ripley', 'Tierney', 'Tukey', 'Venables', 'R Core'), class = 'AsIs'), nationality = structure(c('Australia', 'UK', 'UK', 'US', 'US', 'Australia', NA), class = 'AsIs'), deceased = structure(c('no', 'no', 'no', 'no', 'yes', 'no', NA), class = 'AsIs'), title = structure(c('Interactive Data Analysis', 'Spatial Statistics', 'Stochastic Simulation', 'LISP-STAT', 'Exploratory Data Analysis', 'Modern Applied Statistics ...', 'An Introduction to R'), class = 'AsIs'), other.author = structure(c(NA, NA, NA, NA, NA, 'Ripley', 'Venables & Smith'), class = 'AsIs'));list(argv[[1]],argv[[2]],argv[[3]],argv[[4]],argv[[5]]); [[1]] diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_attrassign.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_attrassign.java index b8b553bad99c00077d46ab1bd2902978b4fab2f2..ab83f538b284c7c7cf469045ea726e1c386b427e 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_attrassign.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_attrassign.java @@ -98,6 +98,12 @@ public class TestBuiltin_attrassign extends TestBase { assertEval("argv <- list(structure(c(0, -187, -34, 0, 165, 0, -95, 121, 107, 0, 41, 0, 0, 93, 0), .Dim = c(5L, 3L)), 'dimnames', value = NULL);`attr<-`(argv[[1]],argv[[2]],argv[[3]]);"); } + @Test + public void testRefCount() { + assertEval("x <- c(1,2); attr(x, \"foo\") <- c(\"a\",\"b\"); y <- x; attr(x,\"foo\")[[1]] <- \"c\"; y"); + assertEval("x <- c(1,2,3); y <- 42; attr(y, 'at') <- x; x[[1]] <- 2; attr(y, 'at')"); + } + @Test public void testArgsCasts() { assertEval(Output.IgnoreErrorContext, "x<-42; attr(x, NULL) <- NULL"); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_list.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_list.java index 0317997c24916e2917819c231054d6cd5b44a61a..e612304d8f81c89851fe5e590a4f2646a6b5b891 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_list.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_list.java @@ -347,6 +347,11 @@ public class TestBuiltin_list extends TestBase { "argv <- list(ANY = structure(function (x, y = NULL) .Internal(crossprod(x, y)), target = structure('ANY', class = structure('signature', package = 'methods'), .Names = 'x', package = 'methods'), defined = structure('ANY', class = structure('signature', package = 'methods'), .Names = 'x', package = 'methods'), generic = structure('crossprod', package = 'base'), class = structure('derivedDefaultMethod', package = 'methods')));list(argv[[1]]);"); } + @Test + public void testRefCount() { + assertEval("{ l <- list(a=c(1,2)); l2 <- l; l$a[[1]] <- 3; l2 }"); + } + @Test public void testList() { assertEval("{ list(a=1, b=2) }");