diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
index e1b29ff4d097c17b20b90f33cd768a97bfffcf2c..d694fbe0c4a3530bf847223c4379d3ee2fccdd9f 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java
@@ -32,10 +32,10 @@ import com.oracle.truffle.r.engine.TruffleRLanguage;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.RCloseable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 
 @MessageResolution(receiverType = RList.class, language = TruffleRLanguage.class)
@@ -106,12 +106,12 @@ public class RListMR {
     @Resolve(message = "KEYS")
     public abstract static class RListKeysNode extends Node {
         @Child private Node findContext = TruffleRLanguage.INSTANCE.actuallyCreateFindContextNode();
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         @SuppressWarnings("try")
         protected Object access(RList receiver) {
             try (RCloseable c = RContext.withinContext(TruffleRLanguage.INSTANCE.actuallyFindContext0(findContext))) {
-                return receiver.getNames(attrProfiles);
+                return getNamesNode.getNames(receiver);
             }
         }
     }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
index 882a6fcf1d1dd0f31aaeb091c4e7c229d3b332e4..8aca802a24be27ab35c04541f29133abeaf55720 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java
@@ -39,7 +39,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExternalPtr;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -307,10 +306,11 @@ public class MethodsListDispatch {
 
         public abstract Object executeObject(String name, REnvironment rho, String pckg);
 
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
         @Child private CastToVectorNode castToVector = CastToVectorNodeGen.create(false);
         @Child private ClassHierarchyScalarNode classHierarchyNode = ClassHierarchyScalarNodeGen.create();
         @Child private PromiseHelperNode promiseHelper;
+        @Child private GetFixedAttributeNode getGenericAttrNode = GetFixedAttributeNode.create(RRuntime.GENERIC_ATTR_KEY);
+        @Child private GetFixedAttributeNode getPckgAttrNode = GetFixedAttributeNode.create(RRuntime.PCKG_ATTR_KEY);
 
         @Specialization
         protected Object getGeneric(String name, REnvironment env, String pckg) {
@@ -327,9 +327,9 @@ public class MethodsListDispatch {
                 if (o != null) {
                     RAttributable vl = (RAttributable) o;
                     boolean ok = false;
-                    if (vl instanceof RFunction && vl.getAttr(attrProfiles, RRuntime.GENERIC_ATTR_KEY) != null) {
+                    if (vl instanceof RFunction && getGenericAttrNode.execute(vl) != null) {
                         if (pckg.length() > 0) {
-                            Object gpckgObj = vl.getAttr(attrProfiles, RRuntime.PCKG_ATTR_KEY);
+                            Object gpckgObj = getPckgAttrNode.execute(vl);
                             if (gpckgObj != null) {
                                 String gpckg = checkSingleString(castToVector.execute(gpckgObj), false, "The \"package\" slot in generic function object", this, classHierarchyNode);
                                 ok = pckg.equals(gpckg);
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
index c2a0cb887709ca2e9ac96630f520e0923c3db8c4..9094f3e34ff8d521a8ec586a20f0686e6d561c10 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cdist.java
@@ -17,7 +17,10 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
 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.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -32,6 +35,8 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 public abstract class Cdist extends RExternalBuiltinNode.Arg4 {
     private static final NACheck naCheck = NACheck.create();
 
+    @Child private GetFixedAttributeNode getNamesAttrNode = GetFixedAttributeNode.createNames();
+
     @Override
     protected void createCasts(CastBuilder casts) {
         casts.arg(0).asDoubleVector();
@@ -43,21 +48,24 @@ public abstract class Cdist extends RExternalBuiltinNode.Arg4 {
     @Specialization(guards = "method == cachedMethod")
     protected RDoubleVector cdist(RAbstractDoubleVector x, @SuppressWarnings("unused") int method, RList list, double p, @SuppressWarnings("unused") @Cached("method") int cachedMethod,
                     @Cached("getMethod(method)") Method methodObj,
-                    @Cached("create()") SetAttributeNode setAttrNode) {
-        int nr = RRuntime.nrows(x);
-        int nc = RRuntime.ncols(x);
+                    @Cached("create()") SetAttributeNode setAttrNode,
+                    @Cached("create()") SetClassAttributeNode setClassAttrNode,
+                    @Cached("create()") GetDimAttributeNode getDimNode) {
+        int nr = getDimNode.nrows(x);
+        int nc = getDimNode.ncols(x);
         int n = nr * (nr - 1) / 2; /* avoid int overflow for N ~ 50,000 */
         double[] ans = new double[n];
         RDoubleVector xm = x.materialize();
         rdistance(xm.getDataWithoutCopying(), nr, nc, ans, false, methodObj, p);
         RDoubleVector result = RDataFactory.createDoubleVector(ans, naCheck.neverSeenNA());
         DynamicObject resultAttrs = result.initAttributes();
-        RStringVector names = (RStringVector) list.getAttr(RRuntime.NAMES_ATTR_KEY);
+
+        RStringVector names = (RStringVector) getNamesAttrNode.execute(list);
         for (int i = 0; i < names.getLength(); i++) {
             String name = names.getDataAt(i);
             Object listValue = list.getDataAt(i);
             if (name.equals(RRuntime.CLASS_ATTR_KEY)) {
-                result.setClassAttr(listValue instanceof RStringVector ? (RStringVector) listValue : RDataFactory.createStringVectorFromScalar((String) listValue));
+                setClassAttrNode.execute(result, listValue instanceof RStringVector ? (RStringVector) listValue : RDataFactory.createStringVectorFromScalar((String) listValue));
             } else {
                 setAttrNode.execute(resultAttrs, name, listValue);
             }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
index 1aff0091ef52ff496d09f462d7c2a1fd8db2e4dd..5080206bbed26ecd787667839af2adae4bfe9f3f 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -76,6 +77,8 @@ public abstract class Covcor extends RExternalBuiltinNode.Arg4 {
     private final BranchProfile error = BranchProfile.create();
     private final BranchProfile warning = BranchProfile.create();
 
+    @Child private GetDimAttributeNode getDimsNode = GetDimAttributeNode.create();
+
     private final LoopConditionProfile loopLength = LoopConditionProfile.createCountingProfile();
 
     public RDoubleVector corcov(RDoubleVector x, RDoubleVector y, @SuppressWarnings("unused") int method, boolean iskendall, RBaseNode invokingNode) throws RError {
@@ -175,14 +178,14 @@ public abstract class Covcor extends RExternalBuiltinNode.Arg4 {
         return ans;
     }
 
-    private static int ncols(RDoubleVector x) {
+    private int ncols(RDoubleVector x) {
         assert x.isMatrix();
-        return x.getDimensions()[1];
+        return getDimsNode.getDimensions(x)[1];
     }
 
-    private static int nrows(RDoubleVector x) {
+    private int nrows(RDoubleVector x) {
         assert x.isMatrix();
-        return x.getDimensions()[0];
+        return getDimsNode.getDimensions(x)[0];
     }
 
     private void complete1(int n, int ncx, RDoubleVector x, RIntVector ind, boolean naFail) {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cutree.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cutree.java
index 2887c8f67d5837b4fe5f76b91940f39a199ac373..8c291da19ee516d98b50441e98361f0af40b22b1 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cutree.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Cutree.java
@@ -10,10 +10,11 @@
  */
 package com.oracle.truffle.r.library.stats;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -29,7 +30,7 @@ public abstract class Cutree extends RExternalBuiltinNode.Arg2 {
     }
 
     @Specialization
-    protected RIntVector cutree(RAbstractIntVector mergeIn, RAbstractIntVector whichIn) {
+    protected RIntVector cutree(RAbstractIntVector mergeIn, RAbstractIntVector whichIn, @Cached("create()") GetDimAttributeNode getDimNode) {
         RIntVector merge = mergeIn.materialize();
         RIntVector which = whichIn.materialize();
         int whichLen = which.getLength();
@@ -43,7 +44,7 @@ public abstract class Cutree extends RExternalBuiltinNode.Arg2 {
         int mm = 0;
         boolean foundJ;
 
-        int n = RRuntime.nrows(merge) + 1;
+        int n = getDimNode.nrows(merge) + 1;
         /*
          * The C code uses 1-based indices for the next three arrays and so set the int * value
          * behind the actual start of the array. To keep the logic equivalent, we call adj(k) on the
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DoubleCentre.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DoubleCentre.java
index 01a1f747831be975f900f91e34525d9d0cc6dd20..b33d733a7cb6b25fac7190bc27dcaca9a1830b73 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DoubleCentre.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/DoubleCentre.java
@@ -10,10 +10,11 @@
  */
 package com.oracle.truffle.r.library.stats;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
 
@@ -25,9 +26,9 @@ public abstract class DoubleCentre extends RExternalBuiltinNode.Arg1 {
     }
 
     @Specialization
-    protected RDoubleVector doubleCentre(RAbstractDoubleVector aVecAbs) {
+    protected RDoubleVector doubleCentre(RAbstractDoubleVector aVecAbs, @Cached("create()") GetDimAttributeNode getDimNode) {
         RDoubleVector aVec = aVecAbs.materialize();
-        int n = RRuntime.nrows(aVec);
+        int n = getDimNode.nrows(aVec);
         double[] a = aVec.getDataWithoutCopying(); // does not copy
 
         for (int i = 0; i < n; i++) {
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
index 0f78a1f32eeb141cbe6370b9e7c4cde94bb49a3e..80d764515eef04f25d408662217f208623651a3e 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/SplineFunctions.java
@@ -43,6 +43,7 @@ public class SplineFunctions {
     }
 
     public abstract static class SplineEval extends RExternalBuiltinNode.Arg2 {
+        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
         @Specialization
         @TruffleBoundary
@@ -90,7 +91,7 @@ public class SplineFunctions {
     /*
      * Periodic Spline --------------- The end conditions here match spline (and its derivatives) at
      * x[1] and x[n].
-     * 
+     *
      * Note: There is an explicit check that the user has supplied data with y[1] equal to y[n].
      */
     private static void periodicSpline(int n, double[] x, double[] y, double[] b, double[] c, double[] d) {
@@ -207,7 +208,7 @@ public class SplineFunctions {
     /*
      * Natural Splines --------------- Here the end-conditions are determined by setting the second
      * derivative of the spline at the end-points to equal to zero.
-     * 
+     *
      * There are n-2 unknowns (y[i]'' at x[2], ..., x[n-1]) and n-2 equations to determine them.
      * Either Choleski or Gaussian elimination could be used.
      */
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
index e856fd478387c79b96ea48c676b782656aa248d3..49204b765ddbc5dee540d31c69888ec6a8c857f2 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/utils/TypeConvert.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
 import java.util.TreeSet;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -37,6 +38,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
 public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
 
+    @Child private SetFixedAttributeNode setLevelsAttrNode = SetFixedAttributeNode.create(RRuntime.LEVELS_ATTR_KEY);
+
     private static boolean isNA(String s, RAbstractStringVector naStrings) {
         // naStrings are in addition to NA_character_
         if (RRuntime.isNA(s)) {
@@ -178,7 +181,7 @@ public abstract class TypeConvert extends RExternalBuiltinNode.Arg5 {
                 }
             }
             RIntVector res = RDataFactory.createIntVector(data, complete);
-            res.setAttr(RRuntime.LEVELS_ATTR_KEY, RDataFactory.createStringVector(levelsArray, RDataFactory.COMPLETE_VECTOR));
+            setLevelsAttrNode.execute(res, RDataFactory.createStringVector(levelsArray, RDataFactory.COMPLETE_VECTOR));
             return RVector.setVectorClassAttr(res, RDataFactory.createStringVector("factor"));
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
index 472fdc3f53b299f573acac83050b028769cddef2..14a7bd1687650116a8d7b22ee82b2c5f6fa1de90 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/APerm.java
@@ -23,13 +23,15 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 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.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -61,10 +63,12 @@ public abstract class APerm extends RBuiltinNode {
     }
 
     @Specialization
-    protected RAbstractVector aPerm(RAbstractVector vector, @SuppressWarnings("unused") RNull permVector, byte resize) {
+    protected RAbstractVector aPerm(RAbstractVector vector, @SuppressWarnings("unused") RNull permVector, byte resize,
+                    @Cached("create()") GetDimAttributeNode getDimsNode,
+                    @Cached("create()") SetDimAttributeNode setDimNode) {
         checkErrorConditions(vector);
 
-        int[] dim = vector.getDimensions();
+        int[] dim = getDimsNode.getDimensions(vector);
         final int diml = dim.length;
 
         RVector<?> result = vector.createEmptySameType(vector.getLength(), vector.isComplete());
@@ -74,9 +78,9 @@ public abstract class APerm extends RBuiltinNode {
             for (int i = 0; i < diml; i++) {
                 pDim[i] = dim[diml - 1 - i];
             }
-            result.setDimensions(pDim);
+            setDimNode.setDimensions(result, pDim);
         } else {
-            result.setDimensions(dim);
+            setDimNode.setDimensions(result, dim);
         }
 
         // Move along the old array using stride
@@ -101,10 +105,12 @@ public abstract class APerm extends RBuiltinNode {
     }
 
     @Specialization
-    protected RAbstractVector aPerm(RAbstractVector vector, RAbstractIntVector permVector, byte resize) {
+    protected RAbstractVector aPerm(RAbstractVector vector, RAbstractIntVector permVector, byte resize,
+                    @Cached("create()") GetDimAttributeNode getDimsNode,
+                    @Cached("create()") SetDimAttributeNode setDimsNode) {
         checkErrorConditions(vector);
 
-        int[] dim = vector.getDimensions();
+        int[] dim = getDimsNode.getDimensions(vector);
         int[] perm = getPermute(dim, permVector);
 
         int[] posV = new int[dim.length];
@@ -112,7 +118,7 @@ public abstract class APerm extends RBuiltinNode {
 
         RVector<?> result = vector.createEmptySameType(vector.getLength(), vector.isComplete());
 
-        result.setDimensions(resize == RRuntime.LOGICAL_TRUE ? pDim : dim);
+        setDimsNode.setDimensions(result, resize == RRuntime.LOGICAL_TRUE ? pDim : dim);
 
         // Move along the old array using stride
         for (int i = 0; i < result.getLength(); i++) {
@@ -126,8 +132,10 @@ public abstract class APerm extends RBuiltinNode {
 
     @Specialization
     protected RAbstractVector aPerm(RAbstractVector vector, RAbstractStringVector permVector, byte resize,
-                    @Cached("create()") RAttributeProfiles dimNamesProfile) {
-        RList dimNames = vector.getDimNames(dimNamesProfile);
+                    @Cached("create()") GetDimAttributeNode getDimsNode,
+                    @Cached("create()") SetDimAttributeNode setDimsNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode) {
+        RList dimNames = getDimNamesNode.getDimNames(vector);
         if (dimNames == null) {
             // TODO: this error is reported after IS_OF_WRONG_LENGTH in GnuR
             errorProfile.enter();
@@ -146,7 +154,7 @@ public abstract class APerm extends RBuiltinNode {
         }
 
         // Note: if this turns out to be slow, we can cache the permutation
-        return aPerm(vector, RDataFactory.createIntVector(perm, true), resize);
+        return aPerm(vector, RDataFactory.createIntVector(perm, true), resize, getDimsNode, setDimsNode);
     }
 
     private static int[] getReverse(int[] dim) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Abbrev.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Abbrev.java
index 7f4e8cce164aac257f239c20c70fec86743ec20c..66b265248d07db9cba90963185681d91f64c38a3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Abbrev.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Abbrev.java
@@ -11,7 +11,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
@@ -20,7 +21,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -29,7 +29,6 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 @RBuiltin(name = "abbreviate", kind = INTERNAL, parameterNames = {"x", "minlength", "use.classes"}, behavior = PURE)
 public abstract class Abbrev extends RBuiltinNode {
     private final NACheck naCheck = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     @Override
     public void createCasts(CastBuilder casts) {
@@ -56,7 +55,7 @@ public abstract class Abbrev extends RBuiltinNode {
             }
         }
         RStringVector result = RDataFactory.createStringVector(data, naCheck.neverSeenNA());
-        result.copyAttributesFrom(attrProfiles, x);
+        result.copyAttributesFrom(x);
         return result;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
index 446875d8fee0449ccf2df866acde5d3cd2edcc2c..cb65b88f22320b3eb45e95941ebca3477833382b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCall.java
@@ -30,13 +30,13 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
@@ -50,7 +50,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 public abstract class AsCall extends RBuiltinNode {
 
     private final ConditionProfile nullNamesProfile = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Specialization
     protected RLanguage asCallFunction(RList x) {
@@ -89,12 +89,12 @@ public abstract class AsCall extends RBuiltinNode {
             values[i] = x.getDataAtAsObject(i + 1);
         }
         ArgumentsSignature signature;
-        if (nullNamesProfile.profile(x.getNames(attrProfiles) == null)) {
+        if (nullNamesProfile.profile(getNamesNode.getNames(x) == null)) {
             signature = ArgumentsSignature.empty(values.length);
         } else {
             String[] names = new String[length];
             // extract names, converting "" to null
-            RStringVector ns = x.getNames(attrProfiles);
+            RStringVector ns = getNamesNode.getNames(x);
             for (int i = 0; i < length; i++) {
                 String name = ns.getDataAt(i + 1);
                 if (name != null && !name.isEmpty()) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
index 1db6100ba110523fc7cfe097d94b81480a216580..aa4fa4aae80e4c3c73d4609f7965677c5bfb9d74 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/AsCharacterFactor.java
@@ -25,8 +25,9 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.dsl.Specialization;
-
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
+
+import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.InheritsNode;
@@ -47,6 +48,7 @@ public abstract class AsCharacterFactor extends RBuiltinNode {
 
     @Child InheritsNode inheritsNode = InheritsNodeGen.create();
     @Child CastToVectorNode castToVectorNode = CastToVectorNode.create();
+    @Child private GetFixedAttributeNode getLevelsAttrNode = GetFixedAttributeNode.create(RRuntime.LEVELS_ATTR_KEY);
 
     @Specialization
     protected RStringVector doAsCharacterFactor(Object x) {
@@ -57,7 +59,7 @@ public abstract class AsCharacterFactor extends RBuiltinNode {
         RIntVector xVec = (RIntVector) x;
         int n = xVec.getLength();
         String[] data = new String[n];
-        Object levsAttr = xVec.getAttr(RRuntime.LEVELS_ATTR_KEY);
+        Object levsAttr = getLevelsAttrNode.execute(xVec);
         Object levs;
         if (levsAttr == null || !((levs = castToVectorNode.execute(levsAttr)) instanceof RAbstractStringVector)) {
             throw RError.error(RError.SHOW_CALLER, RError.Message.MALFORMED_FACTOR);
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 c9e2d6aaf5be2238ac970c9e394ab5d1354f4615..10853a996c8aee8f4c672122aeb90f4cee0f4ebd 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
@@ -31,14 +31,15 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 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.r.nodes.attributes.ArrayAttributeNode;
 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;
@@ -62,7 +63,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class Attr extends RBuiltinNode {
 
     private final ConditionProfile searchPartialProfile = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile errorProfile = BranchProfile.create();
 
     @CompilationFinal private String cachedName = "";
@@ -146,13 +146,14 @@ public abstract class Attr extends RBuiltinNode {
     }
 
     @Specialization(guards = "isRowNamesAttr(name)")
-    protected Object attrRowNames(RAbstractContainer container, @SuppressWarnings("unused") String name, @SuppressWarnings("unused") boolean exact) {
+    protected Object attrRowNames(RAbstractContainer container, @SuppressWarnings("unused") String name, @SuppressWarnings("unused") boolean exact,
+                    @Cached("create()") GetRowNamesAttributeNode getRowNamesNode) {
         // TODO: if exact == false, check for partial match (there is an ignored tests for it)
         DynamicObject attributes = container.getAttributes();
         if (attributes == null) {
             return RNull.instance;
         } else {
-            return getFullRowNames(container.getRowNames(attrProfiles));
+            return getFullRowNames(getRowNamesNode.getRowNames(container));
         }
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
index d5579cf6151b85e0587b23fa51245d3452b81101..41a244f231512aea273734c48b29acbdf8bb28d7 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attributes.java
@@ -35,6 +35,7 @@ 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.r.nodes.attributes.ArrayAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -53,6 +54,7 @@ public abstract class Attributes extends RBuiltinNode {
 
     private final BranchProfile rownamesBranch = BranchProfile.create();
     @Child private ArrayAttributeNode arrayAttrAccess = ArrayAttributeNode.create();
+    @Child private SetNamesAttributeNode setNamesNode = SetNamesAttributeNode.create();
 
     @Specialization
     protected Object attributesNull(RAbstractContainer container, //
@@ -117,11 +119,11 @@ public abstract class Attributes extends RBuiltinNode {
             }
         }
         RList result = RDataFactory.createList(values);
-        result.setNames(RDataFactory.createStringVector(names, true));
+        setNamesNode.setNames(result, RDataFactory.createStringVector(names, true));
         return result;
     }
 
     private static boolean hasAttributes(RAttributable attributable) {
-        return attributable.getAttributes() != null && attributable.getAttributes().size() > 0;
+        return attributable.getAttributes() != null && !attributable.getAttributes().isEmpty();
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
index 8fc0691a76e82d5d36fbb142623805bb51e7f335..0a95c508e5ae397f7717df4e96528108cb1d0c27 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Bind.java
@@ -36,6 +36,11 @@ 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.RASTUtils;
+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.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
@@ -61,7 +66,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -95,6 +99,7 @@ public abstract class Bind extends RBaseNode {
     @Child private CastToVectorNode castVector;
     @Child private UseMethodInternalNode dcn;
     @Child private CastLogicalNode castLogical;
+    @Child private GetDimAttributeNode getDimsNode;
 
     private final BindType type;
 
@@ -103,7 +108,6 @@ public abstract class Bind extends RBaseNode {
     private final ConditionProfile allEmptyVectorProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile nonNullNames = BranchProfile.create();
     private final NACheck naCheck = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     protected final ValueProfile resultProfile = ValueProfile.createClassProfile();
     protected final ValueProfile vectorProfile = ValueProfile.createClassProfile();
 
@@ -122,6 +126,14 @@ public abstract class Bind extends RBaseNode {
         this.type = type;
     }
 
+    protected int[] getVectorDimensions(RAbstractVector v) {
+        if (getDimsNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            getDimsNode = insert(GetDimAttributeNode.create());
+        }
+        return getDimsNode.getDimensions(v);
+    }
+
     protected RAbstractVector castVector(Object value) {
         if (castVector == null) {
             CompilerDirectives.transferToInterpreterAndInvalidate();
@@ -161,7 +173,8 @@ public abstract class Bind extends RBaseNode {
         }
     }
 
-    private Object bindInternal(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, CastNode castNode, boolean needsVectorCast) {
+    private Object bindInternal(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, CastNode castNode, boolean needsVectorCast, SetDimAttributeNode setDimNode,
+                    SetDimNamesAttributeNode setDimNamesNode, GetDimNamesAttributeNode getDimNamesNode, GetNamesAttributeNode getNamesNode) {
         ArgumentsSignature signature = promiseArgs.getSignature();
         String[] vecNames = nullNamesProfile.profile(signature.getNonNullCount() == 0) ? null : new String[signature.getLength()];
         RAbstractVector[] vectors = new RAbstractVector[args.length];
@@ -217,46 +230,70 @@ public abstract class Bind extends RBaseNode {
             }
         }
         if (type == BindType.cbind) {
-            return genericCBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel);
+            return genericCBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
         } else {
-            return genericRBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel);
+            return genericRBind(promiseArgs, vectors, complete, vecNames, naCheck.neverSeenNA(), deparseLevel, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
         }
     }
 
     @Specialization(guards = {"precedence == LOGICAL_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
     protected Object allLogical(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastLogicalNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
+                    @Cached("create()") CastLogicalNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     @Specialization(guards = {"precedence == INT_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
     protected Object allInt(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastIntegerNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
+                    @Cached("create()") CastIntegerNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     @Specialization(guards = {"precedence == DOUBLE_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
     protected Object allDouble(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastDoubleNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
+                    @Cached("create()") CastDoubleNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     @Specialization(guards = {"precedence == STRING_PRECEDENCE", "args.length> 1", "!isDataFrame(args)"})
     protected Object allString(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastStringNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
+                    @Cached("create()") CastStringNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     @Specialization(guards = {"precedence == COMPLEX_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
     protected Object allComplex(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastComplexNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, true);
+                    @Cached("create()") CastComplexNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, true, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     @Specialization(guards = {"precedence == LIST_PRECEDENCE", "args.length > 1", "!isDataFrame(args)"})
     protected Object allList(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence, //
-                    @Cached("create()") CastListNode cast) {
-        return bindInternal(deparseLevel, args, promiseArgs, cast, false);
+                    @Cached("create()") CastListNode cast,
+                    @Cached("create()") SetDimAttributeNode setDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return bindInternal(deparseLevel, args, promiseArgs, cast, false, setDimNode, setDimNamesNode, getDimNamesNode, getNamesNode);
     }
 
     /**
@@ -267,16 +304,16 @@ public abstract class Bind extends RBaseNode {
      * @param dimLength
      * @return dimnames
      */
-    protected Object getDimResultNamesFromElements(RAbstractVector vec, int dimLength, int dimInd) {
+    protected Object getDimResultNamesFromElements(RAbstractVector vec, int dimLength, int dimInd, GetDimNamesAttributeNode getDimNamesNode, GetNamesAttributeNode getNamesNode) {
         Object firstDimResultNames = RNull.instance;
         Object firstDimNames = RNull.instance;
         if (vec.isMatrix()) {
-            RList vecDimNames = vec.getDimNames(attrProfiles);
+            RList vecDimNames = getDimNamesNode.getDimNames(vec);
             if (vecDimNames != null) {
                 firstDimNames = vecDimNames.getDataAt(dimInd);
             }
-        } else if (!vec.isArray() || vec.getDimensions().length == 1) {
-            RStringVector names = vec.getNames(attrProfiles);
+        } else if (!vec.isArray() || getVectorDimensions(vec).length == 1) {
+            RStringVector names = getNamesNode.getNames(vec);
             firstDimNames = names == null ? RNull.instance : names;
         } else {
             RInternalError.unimplemented("binding multi-dimensional arrays is not supported");
@@ -295,11 +332,10 @@ public abstract class Bind extends RBaseNode {
      * by deparsing.
      */
     protected int getDimResultNamesFromVectors(RArgsValuesAndNames promiseArgs, RAbstractVector vec, String[] argNames, int resDim, int oldInd, int vecInd, int deparseLevel,
-                    String[] dimNamesArray,
-                    int dimNamesInd) {
+                    String[] dimNamesArray, int dimNamesInd, GetDimNamesAttributeNode getDimNamesNode) {
         int ind = oldInd;
         if (vec.isMatrix()) {
-            RList vecDimNames = vec.getDimNames(attrProfiles);
+            RList vecDimNames = getDimNamesNode.getDimNames(vec);
             if (vecDimNames != null) {
                 Object resDimNames = vecDimNames.getDataAt(dimNamesInd);
                 if (resDimNames != RNull.instance) {
@@ -319,7 +355,7 @@ public abstract class Bind extends RBaseNode {
                 dimNamesArray[ind++] = RRuntime.NAMES_ATTR_EMPTY_VALUE;
             }
             return -ind;
-        } else if (!vec.isArray() || vec.getDimensions().length == 1) {
+        } else if (!vec.isArray() || getVectorDimensions(vec).length == 1) {
             if (argNames == null) {
                 if (deparseLevel == 0) {
                     dimNamesArray[ind++] = RRuntime.NAMES_ATTR_EMPTY_VALUE;
@@ -377,9 +413,10 @@ public abstract class Bind extends RBaseNode {
     }
 
     protected int[] getDimensions(RAbstractVector vector) {
-        int[] dimensions = vector.getDimensions();
+        RAbstractVector vectorProfiled = vectorProfile.profile(vector);
+        int[] dimensions = getVectorDimensions(vectorProfiled);
         if (dimensions == null || dimensions.length != 2) {
-            return type == BindType.cbind ? new int[]{vector.getLength(), 1} : new int[]{1, vector.getLength()};
+            return type == BindType.cbind ? new int[]{vectorProfiled.getLength(), 1} : new int[]{1, vectorProfiled.getLength()};
         } else {
             assert dimensions.length == 2;
             return dimensions;
@@ -444,7 +481,9 @@ public abstract class Bind extends RBaseNode {
     private final BranchProfile everSeenNotEqualColumns = BranchProfile.create();
 
     @Specialization(guards = {"precedence != NO_PRECEDENCE", "args.length == 1"})
-    protected Object allOneElem(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence) {
+    protected Object allOneElem(int deparseLevel, Object[] args, RArgsValuesAndNames promiseArgs, @SuppressWarnings("unused") int precedence,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         RAbstractVector vec = castVector(args[0]);
         if (vec.isMatrix()) {
             return vec;
@@ -452,7 +491,10 @@ public abstract class Bind extends RBaseNode {
         int[] dims = getDimensions(vec);
         // for cbind dimNamesA is names for the 1st dim and dimNamesB is names for 2nd dim; for
         // rbind the other way around
-        Object dimNamesA = vec.getNames(attrProfiles) == null ? RNull.instance : vec.getNames(attrProfiles);
+        Object dimNamesA = getNamesNode.getNames(vec);
+        if (dimNamesA == null) {
+            dimNamesA = RNull.instance;
+        }
         Object dimNamesB;
 
         ArgumentsSignature signature = promiseArgs.getSignature();
@@ -473,17 +515,19 @@ public abstract class Bind extends RBaseNode {
         }
 
         RVector<?> res = (RVector<?>) vec.copyWithNewDimensions(dims);
-        res.setDimNames(RDataFactory.createList(type == BindType.cbind ? new Object[]{dimNamesA, dimNamesB} : new Object[]{dimNamesB, dimNamesA}));
+        setDimNamesNode.execute(res, RDataFactory.createList(type == BindType.cbind ? new Object[]{dimNamesA, dimNamesB} : new Object[]{dimNamesB, dimNamesA}));
         res.copyRegAttributesFrom(vec);
         return res;
     }
 
-    public RVector<?> genericCBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, int deparseLevel) {
+    public RVector<?> genericCBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, int deparseLevel,
+                    SetDimAttributeNode setDimNode, SetDimNamesAttributeNode setDimNamesNode, GetDimNamesAttributeNode getDimNamesNode, GetNamesAttributeNode getNamesNode) {
 
         int[] resultDimensions = new int[2];
         int[] secondDims = new int[vectors.length];
         boolean notEqualRows = getResultDimensions(vectors, resultDimensions, secondDims);
-        RVector<?> result = resultProfile.profile(vectors[0].createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
+        RAbstractVector first = vectorProfile.profile(vectors[0]);
+        RVector<?> result = resultProfile.profile(first.createEmptySameType(resultDimensions[0] * resultDimensions[1], complete));
 
         int ind = 0;
         Object rowDimResultNames = RNull.instance;
@@ -494,11 +538,11 @@ public abstract class Bind extends RBaseNode {
             RAbstractVector vec = vectorProfile.profile(vectors[i]);
             if (rowDimResultNames == RNull.instance) {
                 // get the first valid names value
-                rowDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[0], 0);
+                rowDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[0], 0, getDimNamesNode, getNamesNode);
             }
 
             // compute dimnames for the second dimension
-            int newColInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, secondDims[i], colInd, i, deparseLevel, colDimNamesArray, 1);
+            int newColInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, secondDims[i], colInd, i, deparseLevel, colDimNamesArray, 1, getDimNamesNode);
             if (newColInd < 0) {
                 colInd = -newColInd;
             } else {
@@ -528,8 +572,8 @@ public abstract class Bind extends RBaseNode {
 
         }
         Object colDimResultNames = allColDimNamesNull ? RNull.instance : RDataFactory.createStringVector(colDimNamesArray, vecNamesComplete);
-        result.setDimensions(resultDimensions);
-        result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
+        setDimNode.setDimensions(result, resultDimensions);
+        setDimNamesNode.setDimNames(result, RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
         return result;
     }
 
@@ -558,7 +602,8 @@ public abstract class Bind extends RBaseNode {
         }
     }
 
-    public RVector<?> genericRBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, int deparseLevel) {
+    public RVector<?> genericRBind(RArgsValuesAndNames promiseArgs, RAbstractVector[] vectors, boolean complete, String[] vecNames, boolean vecNamesComplete, int deparseLevel,
+                    SetDimAttributeNode setDimNode, SetDimNamesAttributeNode setDimNamesNode, GetDimNamesAttributeNode getDimNamesNode, GetNamesAttributeNode getNamesNode) {
 
         int[] resultDimensions = new int[2];
         int[] firstDims = new int[vectors.length];
@@ -574,11 +619,11 @@ public abstract class Bind extends RBaseNode {
             RAbstractVector vec = vectorProfile.profile(vectors[i]);
             if (colDimResultNames == RNull.instance) {
                 // get the first valid names value
-                colDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[1], 1);
+                colDimResultNames = getDimResultNamesFromElements(vec, resultDimensions[1], 1, getDimNamesNode, getNamesNode);
             }
 
             // compute dimnames for the second dimension
-            int newRowInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, firstDims[i], rowInd, i, deparseLevel, rowDimNamesArray, 0);
+            int newRowInd = getDimResultNamesFromVectors(promiseArgs, vec, vecNames, firstDims[i], rowInd, i, deparseLevel, rowDimNamesArray, 0, getDimNamesNode);
             if (newRowInd < 0) {
                 rowInd = -newRowInd;
             } else {
@@ -613,8 +658,8 @@ public abstract class Bind extends RBaseNode {
 
         }
         Object rowDimResultNames = allRowDimNamesNull ? RNull.instance : RDataFactory.createStringVector(rowDimNamesArray, vecNamesComplete);
-        result.setDimensions(resultDimensions);
-        result.setDimNames(RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
+        setDimNode.setDimensions(result, resultDimensions);
+        setDimNamesNode.setDimNames(result, RDataFactory.createList(new Object[]{rowDimResultNames, colDimResultNames}));
         return result;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
index 431cc73c7dddd20f1860c4fa3b9c862136391f6c..99f09dcce3d563e95b0a3292e91bb7de0395d97e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Combine.java
@@ -47,7 +47,8 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
 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.SetFixedAttributeNode;
+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.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.CombineNodeGen.CombineInputCastNodeGen;
@@ -96,7 +97,6 @@ public abstract class Combine extends RBuiltinNode {
     @Child private CombineInputCast inputCast = CombineInputCastNodeGen.create(null);
     @Child private CastToVectorNode castVector;
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile naBranch = BranchProfile.create();
     private final NACheck naCheck = NACheck.create();
     private final ConditionProfile fastNamesMerge = ConditionProfile.createBinaryProfile();
@@ -127,7 +127,8 @@ public abstract class Combine extends RBuiltinNode {
                     @Cached("createCast(cachedPrecedence)") CastNode cast, //
                     @Cached("create()") BranchProfile naNameBranch, //
                     @Cached("create()") NACheck naNameCheck, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile) {
+                    @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         CompilerAsserts.partialEvaluationConstant(cachedSignature);
         CompilerAsserts.partialEvaluationConstant(cachedPrecedence);
 
@@ -142,7 +143,8 @@ public abstract class Combine extends RBuiltinNode {
         // prepare the names (if there are any)
         boolean signatureHasNames = signatureHasNames(cachedSignature);
         CompilerAsserts.partialEvaluationConstant(signatureHasNames);
-        RStringVector namesVector = hasNamesProfile.profile(signatureHasNames || hasNames(elements)) ? foldNames(naNameBranch, naNameCheck, elements, size, cachedSignature) : null;
+        RStringVector namesVector = hasNamesProfile.profile(signatureHasNames || hasNames(elements, getNamesNode)) ? foldNames(naNameBranch, naNameCheck, elements, size, cachedSignature, getNamesNode)
+                        : null;
 
         // get the actual contents of the result
         RVector<?> result = foldContents(cachedPrecedence, elements, size, namesVector);
@@ -159,8 +161,9 @@ public abstract class Combine extends RBuiltinNode {
                     @Cached("createCast(cachedPrecedence)") CastNode cast, //
                     @Cached("create()") BranchProfile naNameBranch, //
                     @Cached("create()") NACheck naNameCheck, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile) {
-        return combineCached(args, false, args.getSignature(), cachedPrecedence, cast, naNameBranch, naNameCheck, hasNamesProfile);
+                    @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return combineCached(args, false, args.getSignature(), cachedPrecedence, cast, naNameBranch, naNameCheck, hasNamesProfile, getNamesNode);
     }
 
     @Specialization(guards = "recursive")
@@ -226,7 +229,7 @@ public abstract class Combine extends RBuiltinNode {
     }
 
     @ExplodeLoop
-    private boolean hasNames(Object[] elements) {
+    private boolean hasNames(Object[] elements, GetNamesAttributeNode getNamesNode) {
         for (int i = 0; i < elements.length; i++) {
             Object element = elements[i];
             if (i < argProfiles.length) {
@@ -234,7 +237,7 @@ public abstract class Combine extends RBuiltinNode {
             }
             if (element instanceof RAbstractVector) {
                 RAbstractVector vector = (RAbstractVector) element;
-                if (vector.getNames(attrProfiles) != null) {
+                if (getNamesNode.getNames(vector) != null) {
                     return true;
                 }
             }
@@ -243,7 +246,7 @@ public abstract class Combine extends RBuiltinNode {
     }
 
     @ExplodeLoop
-    private RStringVector foldNames(BranchProfile naNameBranch, NACheck naNameCheck, Object[] elements, int size, ArgumentsSignature signature) {
+    private RStringVector foldNames(BranchProfile naNameBranch, NACheck naNameCheck, Object[] elements, int size, ArgumentsSignature signature, GetNamesAttributeNode getNamesNode) {
         RStringVector result = RDataFactory.createStringVector(new String[size], true);
         result.incRefCount();
         int pos = 0;
@@ -252,18 +255,19 @@ public abstract class Combine extends RBuiltinNode {
             if (i < argProfiles.length) {
                 element = argProfiles[i].profile(element);
             }
-            pos += processNamesElement(naNameBranch, naNameCheck, result, pos, element, i, signature);
+            pos += processNamesElement(naNameBranch, naNameCheck, result, pos, element, i, signature, getNamesNode);
         }
         return result;
     }
 
-    private int processNamesElement(BranchProfile naNameBranch, NACheck naNameCheck, RStringVector result, int pos, Object element, int index, ArgumentsSignature signature) {
+    private int processNamesElement(BranchProfile naNameBranch, NACheck naNameCheck, RStringVector result, int pos, Object element, int index, ArgumentsSignature signature,
+                    GetNamesAttributeNode getNamesNode) {
         String signatureName = signature.getName(index);
         if (element instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) element;
             int length = v.getLength();
 
-            RStringVector newNames = v.getNames(attrProfiles);
+            RStringVector newNames = getNamesNode.getNames(v);
             if (signatureName != null && length > 0) {
                 if (fastNamesMerge.profile(length == 1 && newNames == null)) {
                     newNames = RDataFactory.createStringVector(new String[]{signatureName}, true);
@@ -455,6 +459,8 @@ public abstract class Combine extends RBuiltinNode {
     protected abstract static class CombineInputCast extends RNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
+        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         public abstract Object execute(Object operand);
 
@@ -474,15 +480,13 @@ public abstract class Combine extends RBuiltinNode {
             RVector<?> materialized = vector.materialize();
             RVector<?> result = materialized.copyDropAttributes();
 
-            RStringVector vecNames = materialized.getInternalNames();
+            RStringVector vecNames = getNamesNode.getNames(materialized);
             if (hasNamesProfile.profile(vecNames != null)) {
                 result.initAttributes(RAttributesLayout.createNames(vecNames));
-                result.setInternalNames(vecNames);
             } else {
-                RList dimNames = materialized.getInternalDimNames();
+                RList dimNames = getDimNamesNode.getDimNames(materialized);
                 if (hasDimNamesProfile.profile(dimNames != null)) {
-                    result.initAttributes(RAttributesLayout.createDimNames(vecNames));
-                    result.setInternalDimNames(dimNames);
+                    result.initAttributes(RAttributesLayout.createDimNames(dimNames));
                 }
             }
             return result;
@@ -494,7 +498,7 @@ public abstract class Combine extends RBuiltinNode {
         }
 
         protected boolean needsCopy(RAbstractVector vector) {
-            return vector.getAttributes() != null || vector.getNames(attrProfiles) != null || vector.getDimNames(attrProfiles) != null;
+            return vector.getAttributes() != null || getNamesNode.getNames(vector) != null || getDimNamesNode.getDimNames(vector) != null;
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
index bd91785573149dbf784c85774719cfbb211c6064..491c440ba79dbbd197522d28fb8ce694809b9ff9 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CopyDFAttr.java
@@ -29,7 +29,6 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -37,12 +36,10 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 @RBuiltin(name = "copyDFattr", kind = INTERNAL, parameterNames = {"", ""}, behavior = COMPLEX)
 public abstract class CopyDFAttr extends RBuiltinNode {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Specialization()
     protected RAttributable copy(RAbstractContainer in, RAbstractVector out) {
         RVector<?> res = out.materialize();
         res.resetAllAttributes(false);
-        return res.copyAttributesFrom(attrProfiles, in);
+        return res.copyAttributesFrom(in);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
index 3aefffb5f6b5d7a8e3bc4f27c5d4f058c0e13fd1..9c6e521f08bfa375abdeec6ab6003d53c8d06ab0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Crossprod.java
@@ -27,7 +27,11 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+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.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -62,21 +66,29 @@ public abstract class Crossprod extends RBuiltinNode {
     }
 
     @Specialization(guards = {"x.isMatrix()", "y.isMatrix()"})
-    protected RDoubleVector crossprod(RAbstractDoubleVector x, RAbstractDoubleVector y) {
-        int xRows = x.getDimensions()[0];
-        int xCols = x.getDimensions()[1];
-        int yRows = y.getDimensions()[0];
-        int yCols = y.getDimensions()[1];
-        return matMult.doubleMatrixMultiply(x, y, xCols, xRows, yRows, yCols, xRows, 1, 1, yRows, false);
+    protected RDoubleVector crossprod(RAbstractDoubleVector x, RAbstractDoubleVector y,
+                    @Cached("create()") GetDimAttributeNode getXDimsNode,
+                    @Cached("create()") GetDimAttributeNode getYDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        int[] xDims = getXDimsNode.getDimensions(x);
+        int[] yDims = getYDimsNode.getDimensions(y);
+        int xRows = xDims[0];
+        int xCols = xDims[1];
+        int yRows = yDims[0];
+        int yCols = yDims[1];
+        return matMult.doubleMatrixMultiply(x, y, xCols, xRows, yRows, yCols, xRows, 1, 1, yRows, false, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
-    private static RDoubleVector mirror(RDoubleVector result) {
+    private static RDoubleVector mirror(RDoubleVector result, GetDimAttributeNode getResultDimsNode) {
         /*
          * Mirroring the result is not only good for performance, but it is also required to produce
          * the same result as GNUR.
          */
-        assert result.isMatrix() && result.getDimensions()[0] == result.getDimensions()[1];
-        int size = result.getDimensions()[0];
+        int[] resultDims = getResultDimsNode.getDimensions(result);
+        assert result.isMatrix() && resultDims[0] == resultDims[1];
+        int size = resultDims[0];
         double[] data = result.getDataWithoutCopying();
         for (int row = 0; row < size; row++) {
             int destIndex = row * size + row + 1;
@@ -96,10 +108,16 @@ public abstract class Crossprod extends RBuiltinNode {
     }
 
     @Specialization(guards = "x.isMatrix()")
-    protected RDoubleVector crossprodDoubleMatrix(RAbstractDoubleVector x, @SuppressWarnings("unused") RNull y) {
-        int xRows = x.getDimensions()[0];
-        int xCols = x.getDimensions()[1];
-        return mirror(matMult.doubleMatrixMultiply(x, x, xCols, xRows, xRows, xCols, xRows, 1, 1, xRows, true));
+    protected RDoubleVector crossprodDoubleMatrix(RAbstractDoubleVector x, @SuppressWarnings("unused") RNull y,
+                    @Cached("create()") GetDimAttributeNode getDimsNode,
+                    @Cached("create()") GetDimAttributeNode getResultDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        int[] xDims = getDimsNode.getDimensions(x);
+        int xRows = xDims[0];
+        int xCols = xDims[1];
+        return mirror(matMult.doubleMatrixMultiply(x, x, xCols, xRows, xRows, xCols, xRows, 1, 1, xRows, true, setDimNamesNode, getADimNamesNode, getBDimNamesNode), getResultDimsNode);
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
index edf8441b58c47ee9bad392bd698d4bc1c72247d7..58cf95204c494529cf4eaff9eeb2710b2657b99c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMax.java
@@ -24,12 +24,12 @@ import java.util.Arrays;
 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.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
@@ -43,7 +43,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 public abstract class CumMax extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -96,7 +96,7 @@ public abstract class CumMax extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(cmaxV, i, cmaxV.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createDoubleVector(cmaxV, na.neverSeenNA(), v.getNames(attrProfiles));
+        return RDataFactory.createDoubleVector(cmaxV, na.neverSeenNA(), getNamesNode.getNames(v));
     }
 
     @Specialization(contains = "cummaxIntSequence")
@@ -118,7 +118,7 @@ public abstract class CumMax extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(cmaxV, i, cmaxV.length, RRuntime.INT_NA);
         }
-        return RDataFactory.createIntVector(cmaxV, na.neverSeenNA(), v.getNames(attrProfiles));
+        return RDataFactory.createIntVector(cmaxV, na.neverSeenNA(), getNamesNode.getNames(v));
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
index a1da7b792e40c8abb45fa505b7b76c03946a281f..b99ad15b12cf41a05b9bb5278ef605be97215766 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumMin.java
@@ -24,12 +24,12 @@ import java.util.Arrays;
 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.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RIntSequence;
@@ -43,7 +43,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 public abstract class CumMin extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -96,7 +96,7 @@ public abstract class CumMin extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(cminV, i, cminV.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createDoubleVector(cminV, na.neverSeenNA(), v.getNames(attrProfiles));
+        return RDataFactory.createDoubleVector(cminV, na.neverSeenNA(), getNamesNode.getNames(v));
     }
 
     @Specialization(contains = "cumminIntSequence")
@@ -118,7 +118,7 @@ public abstract class CumMin extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(cminV, i, cminV.length, RRuntime.INT_NA);
         }
-        return RDataFactory.createIntVector(cminV, na.neverSeenNA(), v.getNames(attrProfiles));
+        return RDataFactory.createIntVector(cminV, na.neverSeenNA(), getNamesNode.getNames(v));
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
index 56cda98a07fd8f9e13be1f2833356ff00be34526..254eaed5fb718f954495b90339e3955362a6ee0e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumProd.java
@@ -24,11 +24,11 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -45,7 +45,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 public abstract class CumProd extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Child private BinaryArithmetic mul = BinaryArithmetic.MULTIPLY.createOperation();
 
@@ -88,7 +88,7 @@ public abstract class CumProd extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(array, i, array.length, RRuntime.INT_NA);
         }
-        return RDataFactory.createIntVector(array, !na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createIntVector(array, !na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 
     @Specialization
@@ -110,7 +110,7 @@ public abstract class CumProd extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(array, i, array.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createDoubleVector(array, !na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createDoubleVector(array, !na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 
     @Specialization
@@ -133,6 +133,6 @@ public abstract class CumProd extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(array, 2 * i, array.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createComplexVector(array, !na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createComplexVector(array, !na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
index 06c1be88b75946797245a613f51c0f5a2578668a..3b6074626f3ab1dc0fc1aedf94739337cd75964d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/CumSum.java
@@ -36,11 +36,11 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import java.util.Arrays;
 
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -58,7 +58,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck;
 public abstract class CumSum extends RBuiltinNode {
 
     private final NACheck na = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Child private BinaryArithmetic add = BinaryArithmetic.ADD.createOperation();
 
@@ -100,7 +100,7 @@ public abstract class CumSum extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(res, i, res.length, RRuntime.INT_NA);
         }
-        return RDataFactory.createIntVector(res, na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createIntVector(res, na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 
     @Specialization
@@ -119,7 +119,7 @@ public abstract class CumSum extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(res, i, res.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createDoubleVector(res, na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createDoubleVector(res, na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 
     @Specialization
@@ -141,7 +141,7 @@ public abstract class CumSum extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(res, i, res.length, RRuntime.INT_NA);
         }
-        return RDataFactory.createIntVector(res, na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createIntVector(res, na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 
     @Specialization
@@ -161,6 +161,6 @@ public abstract class CumSum extends RBuiltinNode {
         if (!na.neverSeenNA()) {
             Arrays.fill(res, 2 * i, res.length, RRuntime.DOUBLE_NA);
         }
-        return RDataFactory.createComplexVector(res, na.neverSeenNA(), arg.getNames(attrProfiles));
+        return RDataFactory.createComplexVector(res, na.neverSeenNA(), getNamesNode.getNames(arg));
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
index c9a3e2826bcf8d325844e894e04922fb2c9f369a..c45cb05324403c8247e900ca5c64b8162defb836 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DatePOSIXFunctions.java
@@ -36,7 +36,10 @@ import java.util.HashMap;
 import java.util.TimeZone;
 
 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.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -137,6 +140,7 @@ public class DatePOSIXFunctions {
     public abstract static class Date2POSIXlt extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -161,7 +165,7 @@ public class DatePOSIXFunctions {
                 }
             }
             RList result = builder.finish();
-            RStringVector xNames = x.getNames(attrProfiles);
+            RStringVector xNames = getNamesNode.getNames(x);
             if (xNames != null) {
                 ((RIntVector) result.getDataAt(5)).copyNamesFrom(attrProfiles, x);
             }
@@ -173,6 +177,7 @@ public class DatePOSIXFunctions {
     public abstract static class AsPOSIXlt extends RBuiltinNode {
 
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         @Override
         protected void createCasts(CastBuilder casts) {
@@ -203,7 +208,7 @@ public class DatePOSIXFunctions {
                 }
             }
             RList result = builder.finish();
-            RStringVector xNames = x.getNames(attrProfiles);
+            RStringVector xNames = getNamesNode.getNames(x);
             if (xNames != null) {
                 ((RIntVector) result.getDataAt(5)).copyNamesFrom(attrProfiles, x);
             }
@@ -280,7 +285,8 @@ public class DatePOSIXFunctions {
 
         @Specialization
         @TruffleBoundary
-        protected RDoubleVector posix2date(RAbstractListVector x) {
+        protected RDoubleVector posix2date(RAbstractListVector x,
+                        @Cached("create()") SetClassAttributeNode setClassAttrNode) {
             RAbstractVector secVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(0));
             RAbstractVector minVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(1));
             RAbstractVector hourVector = (RAbstractVector) RRuntime.asAbstractVector(x.getDataAt(2));
@@ -314,7 +320,7 @@ public class DatePOSIXFunctions {
                 }
             }
             RDoubleVector result = RDataFactory.createDoubleVector(data, complete);
-            result.setClassAttr(CLASS_ATTR);
+            setClassAttrNode.execute(result, CLASS_ATTR);
             return result;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
index be4d0db5ec9a83264b3daeaf3390b780184ef2eb..95cb519bd8e14fe633c8a23426a03118a603379e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Dim.java
@@ -29,6 +29,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -40,9 +41,11 @@ public abstract class Dim extends RBuiltinNode {
 
     @Specialization
     protected Object dim(RAbstractContainer container, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasDimensionsProfile) {
-        if (hasDimensionsProfile.profile(container.hasDimensions())) {
-            return RDataFactory.createIntVector(container.getDimensions(), RDataFactory.COMPLETE_VECTOR);
+                    @Cached("createBinaryProfile()") ConditionProfile hasDimensionsProfile,
+                    @Cached("create()") GetDimAttributeNode getDimsNode) {
+        int[] dims = getDimsNode.getDimensions(container);
+        if (hasDimensionsProfile.profile(dims != null && dims.length > 0)) {
+            return RDataFactory.createIntVector(dims, RDataFactory.COMPLETE_VECTOR);
         } else {
             return RNull.instance;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
index 17661779644a7b819009377f238888db6a8a9fde..6953af8262f8466702d2e38fc0a2a5af330a1b52 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DimNames.java
@@ -26,11 +26,12 @@ import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+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.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
@@ -38,7 +39,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 @RBuiltin(name = "dimnames", kind = PRIMITIVE, parameterNames = {"x"}, dispatch = INTERNAL_GENERIC, behavior = PURE)
 public abstract class DimNames extends RBuiltinNode {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final ConditionProfile nullProfile = ConditionProfile.createBinaryProfile();
 
     @Specialization(guards = "!isRAbstractContainer(operand)")
@@ -47,8 +47,8 @@ public abstract class DimNames extends RBuiltinNode {
     }
 
     @Specialization
-    protected Object getDimNames(RAbstractContainer container) {
-        RList names = container.getDimNames(attrProfiles);
+    protected Object getDimNames(RAbstractContainer container, @Cached("create()") GetDimNamesAttributeNode getDimNamesNode) {
+        RList names = getDimNamesNode.getDimNames(container);
         return nullProfile.profile(names == null) ? RNull.instance : names;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
index 7cb11a03f335315d7421134e3bb055a720266eed..dbcf174805a15697847c63c09643e27572a98e02 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DoCall.java
@@ -37,6 +37,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.FrameSlotNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.GetFunctionsFactory.GetNodeGen;
@@ -50,7 +51,6 @@ import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.REmpty;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -72,8 +72,8 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode
 
     @Child private GetFunctions.Get getNode;
     @Child private GetCallerFrameNode getCallerFrame;
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile containsRLanguageProfile = BranchProfile.create();
     private final BranchProfile containsRSymbolProfile = BranchProfile.create();
 
@@ -113,7 +113,7 @@ public abstract class DoCall extends RBuiltinNode implements InternalRSyntaxNode
          * To re-create the illusion of a normal call, turn the values in argsAsList into promises.
          */
         Object[] argValues = argsAsList.getDataCopy();
-        RStringVector n = argsAsList.getNames(attrProfiles);
+        RStringVector n = getNamesNode.getNames(argsAsList);
         ArgumentsSignature signature;
         if (n == null) {
             signature = ArgumentsSignature.empty(argValues.length);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
index 43cfb6a97d7bde5e346af117e8b0e2762f640d5d..7104921f0dc2c92786597d31e9c707f6fadf5f1c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
@@ -25,9 +25,15 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 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.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -47,8 +53,13 @@ public abstract class Drop extends RBuiltinNode {
     private final ConditionProfile noDimNamesProfile = ConditionProfile.createBinaryProfile();
 
     @Specialization
-    protected RAbstractVector doDrop(RAbstractVector x) {
-        int[] dims = x.getDimensions();
+    protected RAbstractVector doDrop(RAbstractVector x,
+                    @Cached("create()") GetDimAttributeNode getDimsNode,
+                    @Cached("create()") SetDimAttributeNode setDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimsNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimsNamesNode,
+                    @Cached("create()") SetNamesAttributeNode setNamesNode) {
+        int[] dims = getDimsNode.getDimensions(x);
         if (nullDimensions.profile(dims == null)) {
             return x;
         }
@@ -67,15 +78,15 @@ public abstract class Drop extends RBuiltinNode {
         if (resultIsScalarProfile.profile(lastNonOneIndex == -1)) {
             @SuppressWarnings("unused")
             RAbstractVector r = x.copy();
-            x.setDimensions(null);
-            x.setDimNames(null);
-            x.setNames(null);
+            setDimsNode.setDimensions(x, null);
+            setDimsNamesNode.setDimNames(x, null);
+            setNamesNode.setNames(x, null);
             return x;
         }
 
         // the result is vector
         if (resultIsVector.profile(newDimsLength <= 1)) {
-            return toVector(x, lastNonOneIndex);
+            return toVector(x, lastNonOneIndex, setDimsNode, getDimsNamesNode, setNamesNode);
         }
 
         // else: the result will be a matrix, copy non-1 dimensions
@@ -88,10 +99,10 @@ public abstract class Drop extends RBuiltinNode {
         }
 
         RAbstractVector result = x.copy();
-        result.setDimensions(newDims);
+        setDimsNode.setDimensions(result, newDims);
 
         // if necessary, copy corresponding dimnames
-        RList oldDimNames = x.getDimNames(dimNamesAttrProfile);
+        RList oldDimNames = getDimsNamesNode.getDimNames(x);
         if (noDimNamesProfile.profile(oldDimNames != null)) {
             newDimsIdx = 0;
             Object[] newDimNames = new Object[newDimsLength];
@@ -100,9 +111,9 @@ public abstract class Drop extends RBuiltinNode {
                     newDimNames[newDimsIdx++] = oldDimNames.getDataAt(i);
                 }
             }
-            result.setDimNames(RDataFactory.createList(newDimNames));
+            setDimsNamesNode.setDimNames(result, RDataFactory.createList(newDimNames));
         } else {
-            result.setDimNames(null);
+            setDimsNamesNode.setDimNames(result, null);
         }
 
         return result;
@@ -112,14 +123,14 @@ public abstract class Drop extends RBuiltinNode {
      * Handles the case when result is just a vector. The only catch is that we might have to copy
      * corresponding index from dimnames to names attribute of the new vector.
      */
-    private RAbstractVector toVector(RAbstractVector x, int nonOneIndex) {
+    private RAbstractVector toVector(RAbstractVector x, int nonOneIndex, SetDimAttributeNode setDimsNode, GetDimNamesAttributeNode getDimNamesNode, SetNamesAttributeNode setNamesNode) {
         RAbstractVector result = x.copy(); // share?
-        result.setDimensions(null);
+        setDimsNode.setDimensions(result, null);
 
         // copy dimnames to names if possible
-        RList dimNames = x.getDimNames(dimNamesAttrProfile);
+        RList dimNames = getDimNamesNode.getDimNames(x);
         if (noDimNamesProfile.profile(dimNames != null) && nonOneIndex < dimNames.getLength()) {
-            result.setNames(ensureStringVector(dimNames.getDataAt(nonOneIndex)));
+            setNamesNode.setNames(result, ensureStringVector(dimNames.getDataAt(nonOneIndex)));
         }
 
         return result;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
index 91b440abc58300884c1fe7ce11b3eb9cdef5690f..943bd6ee74737e7f78df1db20db0fa42b51ab274 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DynLoadFunctions.java
@@ -22,7 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
@@ -32,6 +36,7 @@ import java.util.ArrayList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -102,6 +107,9 @@ public class DynLoadFunctions {
 
     @RBuiltin(name = "getLoadedDLLs", kind = INTERNAL, parameterNames = {}, behavior = READS_STATE)
     public abstract static class GetLoadedDLLs extends RBuiltinNode {
+
+        @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
         @Specialization
         @TruffleBoundary
         protected RList doGetLoadedDLLs() {
@@ -115,7 +123,7 @@ public class DynLoadFunctions {
                 data[i] = dllInfo.toRList();
             }
             RList result = RDataFactory.createList(data, RDataFactory.createStringVector(names, RDataFactory.COMPLETE_VECTOR));
-            result.setClassAttr(RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS));
+            setClassAttrNode.execute(result, RDataFactory.createStringVectorFromScalar(DLLINFOLIST_CLASS));
             return result;
         }
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
index e5ae35b08c784065f46fd10c6b38abd451044f51..d7188cf48b1d06d525c8c64352f9844d7f4d5d0c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/EnvFunctions.java
@@ -49,6 +49,8 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.nodes.RRootNode;
+import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.RList2EnvNode;
@@ -172,10 +174,14 @@ public class EnvFunctions {
             return list2Env.execute(list, env);
         }
 
+        protected GetFixedAttributeNode createGetXDataAttrNode() {
+            return GetFixedAttributeNode.create(RRuntime.DOT_XDATA);
+        }
+
         @Specialization
-        protected Object asEnvironment(RS4Object obj) {
+        protected Object asEnvironment(RS4Object obj, @Cached("createGetXDataAttrNode()") GetFixedAttributeNode getXDataAttrNode) {
             // generic dispatch tried already
-            Object xData = obj.getAttr(RRuntime.DOT_XDATA);
+            Object xData = getXDataAttrNode.execute(obj);
             if (xData == null || !(xData instanceof REnvironment)) {
                 throw RError.error(this, RError.Message.S4OBJECT_NX_ENVIRONMENT);
             } else {
@@ -319,8 +325,8 @@ public class EnvFunctions {
     @RBuiltin(name = "environment", kind = INTERNAL, parameterNames = {"fun"}, behavior = COMPLEX)
     public abstract static class Environment extends RBuiltinNode {
 
-        private static RAttributeProfiles attributeProfile = RAttributeProfiles.create();
         private final ConditionProfile attributable = ConditionProfile.createBinaryProfile();
+        @Child private GetFixedAttributeNode getEnvAttrNode;
 
         @Specialization
         protected Object environment(VirtualFrame frame, @SuppressWarnings("unused") RNull fun,
@@ -352,16 +358,24 @@ public class EnvFunctions {
         }
 
         @Specialization(guards = "isRFormula(formula)")
-        protected Object environment(RLanguage formula,
-                        @Cached("create()") RAttributeProfiles attrProfiles) {
-            Object result = formula.getAttr(attrProfiles, RRuntime.DOT_ENVIRONMENT);
+        protected Object environment(RLanguage formula) {
+            if (getEnvAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getEnvAttrNode = insert(GetFixedAttributeNode.create(RRuntime.DOT_ENVIRONMENT));
+            }
+
+            Object result = getEnvAttrNode.execute(formula);
             return result == null ? RNull.instance : result;
         }
 
         @Specialization(guards = {"!isRNull(fun)", "!isRFunction(fun)", "!isRFormula(fun)"})
         protected Object environment(Object fun) {
             if (attributable.profile(fun instanceof RAttributable)) {
-                Object attr = ((RAttributable) fun).getAttr(attributeProfile, RRuntime.DOT_ENVIRONMENT);
+                if (getEnvAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    getEnvAttrNode = insert(GetFixedAttributeNode.create(RRuntime.DOT_ENVIRONMENT));
+                }
+                Object attr = getEnvAttrNode.execute(fun);
                 return attr == null ? RNull.instance : attr;
             } else {
                 // Not an error according to GnuR
@@ -401,16 +415,22 @@ public class EnvFunctions {
             throw RError.error(this, RError.Message.USE_NULL_ENV_DEFUNCT);
         }
 
+        protected SetFixedAttributeNode createSetEnvAttrNode() {
+            return SetFixedAttributeNode.create(RRuntime.DOT_ENVIRONMENT);
+        }
+
         @Specialization
         @TruffleBoundary
-        protected static Object updateEnvironment(RAbstractContainer obj, REnvironment env) {
-            return updateEnvironment((RAttributable) obj, env);
+        protected static Object updateEnvironment(RAbstractContainer obj, REnvironment env,
+                        @Cached("createSetEnvAttrNode()") SetFixedAttributeNode setEnvAttrNode) {
+            return updateEnvironment((RAttributable) obj, env, setEnvAttrNode);
         }
 
         @Specialization
         @TruffleBoundary
-        protected static Object updateEnvironment(RAttributable obj, REnvironment env) {
-            obj.setAttr(RRuntime.DOT_ENVIRONMENT, env);
+        protected static Object updateEnvironment(RAttributable obj, REnvironment env,
+                        @Cached("createSetEnvAttrNode()") SetFixedAttributeNode setEnvAttrNode) {
+            setEnvAttrNode.execute(obj, env);
             return obj;
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
index 83bade1e3165ab0807caa023896b8a47890d2a4c..63d8b68047883562b4511b147b70663fb83c40ca 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FileFunctions.java
@@ -57,15 +57,16 @@ import java.util.stream.Stream;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
-import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBehavior;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
@@ -266,6 +267,8 @@ public class FileFunctions {
         private static final RStringVector NAMES_VECTOR = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
         private static final RStringVector OCTMODE = RDataFactory.createStringVectorFromScalar("octmode");
 
+        @Child private SetClassAttributeNode setClassAttrNode;
+
         @Override
         protected void createCasts(CastBuilder casts) {
             casts.arg("extra_cols").asLogicalVector().findFirst().map(toBoolean());
@@ -400,12 +403,19 @@ public class FileFunctions {
             // @formatter:on
         }
 
-        private static Object createColumnResult(Column column, Object data, boolean complete) {
+        private Object createColumnResult(Column column, Object data, boolean complete) {
             // @formatter:off
             switch(column) {
                 case size: return RDataFactory.createDoubleVector((double[]) data, complete);
                 case isdir: return RDataFactory.createLogicalVector((byte[]) data, complete);
-                case mode: RIntVector res = RDataFactory.createIntVector((int[]) data, complete); res.setClassAttr(OCTMODE); return res;
+                case mode:
+                    if (setClassAttrNode == null) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        setClassAttrNode = insert(SetClassAttributeNode.create());
+                    }
+                    RIntVector res = RDataFactory.createIntVector((int[]) data, complete);
+                    setClassAttrNode.execute(res, OCTMODE);
+                    return res;
                 case mtime: case ctime: case atime:
                 case uid: case gid: return RDataFactory.createIntVector((int[]) data, complete);
                 case uname: case grname: return RDataFactory.createStringVector((String[]) data, complete);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
index 0ff9871785cb6fb0523d27ae4bae2b6f98f303e7..c69f2dac6af092d2932465d0d8d60fb9974cf2ee 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/FormatC.java
@@ -15,7 +15,9 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
@@ -51,8 +53,10 @@ public abstract class FormatC extends RBuiltinNode {
 
     @SuppressWarnings("unused")
     @Specialization
-    RAttributable formatC(RAbstractContainer x, String mode, int width, int digits, String format, String flag, int iStrlen) {
+    RAttributable formatC(RAbstractContainer x, String mode, int width, int digits, String format, String flag, int iStrlen,
+                    @Cached("create()") SetClassAttributeNode setClassAttrNode) {
         RStringVector res = castStringVector(x);
-        return res.setClassAttr(null);
+        setClassAttrNode.reset(res);
+        return res;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
index fbaf2c1d0ed89cda0558bcd0e1b6be5d675a333b..f873109da07b8215fc2cbf199541c0ec6dd8e2df 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java
@@ -11,7 +11,8 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notEmpty;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
@@ -26,6 +27,7 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -473,7 +475,7 @@ public class GrepFunctions {
                     result[i] = value;
                 }
                 RStringVector ret = RDataFactory.createStringVector(result, vector.isComplete());
-                ret.copyAttributesFrom(attrProfiles, vector);
+                ret.copyAttributesFrom(vector);
                 return ret;
             } catch (PatternSyntaxException e) {
                 CompilerDirectives.transferToInterpreter();
@@ -664,6 +666,13 @@ public class GrepFunctions {
     @RBuiltin(name = "regexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class Regexp extends CommonCodeAdapter {
 
+        @Child SetFixedAttributeNode setMatchLengthAttrNode = SetFixedAttributeNode.create("match.length");
+        @Child SetFixedAttributeNode setUseBytesAttrNode = SetFixedAttributeNode.create("useBytes");
+        @Child SetFixedAttributeNode setCaptureStartAttrNode = SetFixedAttributeNode.create("capture.start");
+        @Child SetFixedAttributeNode setCaptureLengthAttrNode = SetFixedAttributeNode.create("capture.length");
+        @Child SetFixedAttributeNode setCaptureNamesAttrNode = SetFixedAttributeNode.create("capture.names");
+        @Child SetFixedAttributeNode setDimNamesAttrNode = SetFixedAttributeNode.createDimNames();
+
         @Override
         protected void createCasts(CastBuilder casts) {
             castPattern(casts);
@@ -755,19 +764,19 @@ public class GrepFunctions {
                 }
             }
             RIntVector ret = RDataFactory.createIntVector(result, RDataFactory.COMPLETE_VECTOR);
-            ret.setAttr("match.length", RDataFactory.createIntVector(matchLength, RDataFactory.COMPLETE_VECTOR));
+            setMatchLengthAttrNode.execute(ret, RDataFactory.createIntVector(matchLength, RDataFactory.COMPLETE_VECTOR));
             if (useBytes) {
-                ret.setAttr("useBytes", RRuntime.LOGICAL_TRUE);
+                setUseBytesAttrNode.execute(ret, RRuntime.LOGICAL_TRUE);
             }
             if (hasAnyCapture) {
                 RStringVector captureNamesVec = RDataFactory.createStringVector(captureNames, RDataFactory.COMPLETE_VECTOR);
                 RIntVector captureStartVec = RDataFactory.createIntVector(captureStart, RDataFactory.COMPLETE_VECTOR, new int[]{vector.getLength(), captureNames.length});
-                captureStartVec.setAttr(RRuntime.DIMNAMES_ATTR_KEY, RDataFactory.createList(new Object[]{RNull.instance, captureNamesVec.copy()}));
-                ret.setAttr("capture.start", captureStartVec);
+                setDimNamesAttrNode.execute(captureStartVec, RDataFactory.createList(new Object[]{RNull.instance, captureNamesVec.copy()}));
+                setCaptureStartAttrNode.execute(ret, captureStartVec);
                 RIntVector captureLengthVec = RDataFactory.createIntVector(captureLength, RDataFactory.COMPLETE_VECTOR, new int[]{vector.getLength(), captureNames.length});
-                captureLengthVec.setAttr(RRuntime.DIMNAMES_ATTR_KEY, RDataFactory.createList(new Object[]{RNull.instance, captureNamesVec.copy()}));
-                ret.setAttr("capture.length", captureLengthVec);
-                ret.setAttr("capture.names", captureNamesVec);
+                setDimNamesAttrNode.execute(captureLengthVec, RDataFactory.createList(new Object[]{RNull.instance, captureNamesVec.copy()}));
+                setCaptureLengthAttrNode.execute(ret, captureLengthVec);
+                setCaptureNamesAttrNode.execute(ret, captureNamesVec);
             }
             return ret;
         }
@@ -845,6 +854,13 @@ public class GrepFunctions {
     @RBuiltin(name = "gregexpr", kind = INTERNAL, parameterNames = {"pattern", "text", "ignore.case", "perl", "fixed", "useBytes"}, behavior = PURE)
     public abstract static class Gregexpr extends Regexp {
 
+        @Child SetFixedAttributeNode setMatchLengthAttrNode = SetFixedAttributeNode.create("match.length");
+        @Child SetFixedAttributeNode setUseBytesAttrNode = SetFixedAttributeNode.create("useBytes");
+        @Child SetFixedAttributeNode setCaptureStartAttrNode = SetFixedAttributeNode.create("capture.start");
+        @Child SetFixedAttributeNode setCaptureLengthAttrNode = SetFixedAttributeNode.create("capture.length");
+        @Child SetFixedAttributeNode setCaptureNamesAttrNode = SetFixedAttributeNode.create("capture.names");
+        @Child SetFixedAttributeNode setDimNamesAttrNode = SetFixedAttributeNode.createDimNames();
+
         @Override
         protected void createCasts(CastBuilder casts) {
             castPattern(casts);
@@ -855,19 +871,19 @@ public class GrepFunctions {
             castUseBytes(casts);
         }
 
-        private static void setNoCaptureAttributes(RIntVector vec, RStringVector captureNames) {
+        private void setNoCaptureAttributes(RIntVector vec, RStringVector captureNames) {
             int len = captureNames.getLength();
             int[] captureStartData = new int[len];
             int[] captureLengthData = new int[len];
             Arrays.fill(captureStartData, -1);
             Arrays.fill(captureLengthData, -1);
             RIntVector captureStart = RDataFactory.createIntVector(captureStartData, RDataFactory.COMPLETE_VECTOR, new int[]{1, captureNames.getLength()});
-            captureStart.setAttr(RRuntime.DIMNAMES_ATTR_KEY, RDataFactory.createList(new Object[]{RNull.instance, captureNames.copy()}));
+            setDimNamesAttrNode.execute(captureStart, RDataFactory.createList(new Object[]{RNull.instance, captureNames.copy()}));
             RIntVector captureLength = RDataFactory.createIntVector(captureLengthData, RDataFactory.COMPLETE_VECTOR, new int[]{1, captureNames.getLength()});
-            captureLength.setAttr(RRuntime.DIMNAMES_ATTR_KEY, RDataFactory.createList(new Object[]{RNull.instance, captureNames.copy()}));
-            vec.setAttr("capture.start", captureStart);
-            vec.setAttr("capture.length", captureLength);
-            vec.setAttr("capture.names", captureNames);
+            setDimNamesAttrNode.execute(captureLength, RDataFactory.createList(new Object[]{RNull.instance, captureNames.copy()}));
+            setCaptureStartAttrNode.execute(vec, captureStart);
+            setCaptureLengthAttrNode.execute(vec, captureLength);
+            setCaptureNamesAttrNode.execute(vec, captureNames);
         }
 
         @Specialization
@@ -899,16 +915,16 @@ public class GrepFunctions {
                     for (int j = 0; j < txt.length(); j++) {
                         res.setDataAt(res.getDataWithoutCopying(), j, j + 1);
                     }
-                    res.setAttr("match.length", RDataFactory.createIntVector(txt.length()));
+                    setMatchLengthAttrNode.execute(res, RDataFactory.createIntVector(txt.length()));
                     if (useBytes) {
-                        res.setAttr("useBytes", RRuntime.LOGICAL_TRUE);
+                        setUseBytesAttrNode.execute(res, RRuntime.LOGICAL_TRUE);
                     }
                 } else {
                     List<Info> l = getInfo(pattern, vector.getDataAt(i), ignoreCase, perl, fixed);
                     res = toIndexOrSizeVector(l, true);
-                    res.setAttr("match.length", toIndexOrSizeVector(l, false));
+                    setMatchLengthAttrNode.execute(res, toIndexOrSizeVector(l, false));
                     if (useBytes) {
-                        res.setAttr("useBytes", RRuntime.LOGICAL_TRUE);
+                        setUseBytesAttrNode.execute(res, RRuntime.LOGICAL_TRUE);
                     }
                     RIntVector captureStart = toCaptureStartOrLength(l, true);
                     if (captureStart != null) {
@@ -923,9 +939,9 @@ public class GrepFunctions {
                             }
                         }
                         hasAnyCapture = true;
-                        res.setAttr("capture.start", captureStart);
-                        res.setAttr("capture.length", captureLength);
-                        res.setAttr("capture.names", captureNames);
+                        setCaptureStartAttrNode.execute(res, captureStart);
+                        setCaptureLengthAttrNode.execute(res, captureLength);
+                        setCaptureNamesAttrNode.execute(res, captureNames);
                     } else if (hasAnyCapture) {
                         assert captureNames != null;
                         // it's capture names from previous iteration, so copy
@@ -947,7 +963,7 @@ public class GrepFunctions {
             return RDataFactory.createIntVector(arr, RDataFactory.COMPLETE_VECTOR);
         }
 
-        private static RIntVector toCaptureStartOrLength(List<Info> list, boolean start) {
+        private RIntVector toCaptureStartOrLength(List<Info> list, boolean start) {
             assert list.size() > 0;
             Info firstInfo = list.get(0);
             if (!firstInfo.hasCapture) {
@@ -966,7 +982,7 @@ public class GrepFunctions {
                 }
             }
             RIntVector ret = RDataFactory.createIntVector(arr, RDataFactory.COMPLETE_VECTOR, new int[]{list.size(), firstInfo.captureNames.length});
-            ret.setAttr(RRuntime.DIMNAMES_ATTR_KEY, RDataFactory.createList(new Object[]{RNull.instance, RDataFactory.createStringVector(firstInfo.captureNames, RDataFactory.COMPLETE_VECTOR)}));
+            setDimNamesAttrNode.execute(ret, RDataFactory.createList(new Object[]{RNull.instance, RDataFactory.createStringVector(firstInfo.captureNames, RDataFactory.COMPLETE_VECTOR)}));
             return ret;
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
index 7b1275c1b1d5ffbe7a3539868b2d97a1a451e9a4..d04b57fd08082c853c5738796cd4fa9a5d2f151a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/HiddenInternalFunctions.java
@@ -27,6 +27,7 @@ import java.nio.ByteBuffer;
 
 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.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -35,6 +36,7 @@ import com.oracle.truffle.api.nodes.LoopNode;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.access.ConstantNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.PromiseHelperNode;
@@ -284,7 +286,8 @@ public class HiddenInternalFunctions {
 
         @Specialization(guards = "isDLLInfo(externalPtr)")
         @TruffleBoundary
-        protected RList getRegisteredRoutines(RExternalPtr externalPtr) {
+        protected RList getRegisteredRoutines(RExternalPtr externalPtr,
+                        @Cached("create()") SetClassAttributeNode setClassAttrNode) {
             Object[] data = new Object[NAMES.getLength()];
             DLL.DLLInfo dllInfo = (DLLInfo) externalPtr.getExternalObject();
             RInternalError.guarantee(dllInfo != null);
@@ -301,7 +304,7 @@ public class HiddenInternalFunctions {
                     symbolData[i] = symbolInfo.createRSymbolObject(rnt, true);
                 }
                 RList symbolDataList = RDataFactory.createList(symbolData);
-                symbolDataList.setClassAttr(NATIVE_ROUTINE_LIST);
+                setClassAttrNode.execute(symbolDataList, NATIVE_ROUTINE_LIST);
                 data[nst.ordinal()] = symbolDataList;
             }
             return RDataFactory.createList(data, NAMES);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
index 984935f3b56ac9def0cdc4c4fcc437aafa1d79a3..4a85c00052164f5c2ea76442cfab799fb2cdf6ea 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsNA.java
@@ -31,11 +31,14 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 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.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -54,8 +57,11 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class IsNA extends RBuiltinNode {
 
     @Child private IsNA recursiveIsNA;
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
+    @Child private GetDimAttributeNode getDimsNode = GetDimAttributeNode.create();
+    @Child private SetDimNamesAttributeNode setDimNamesNode = SetDimNamesAttributeNode.create();
+    @Child private GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final ConditionProfile nullDimNamesProfile = ConditionProfile.createBinaryProfile();
 
     private Object isNARecursive(Object o) {
@@ -194,10 +200,10 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     private RLogicalVector createResult(byte[] data, RAbstractVector originalVector) {
-        RLogicalVector result = RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR, originalVector.getDimensions(), originalVector.getNames(attrProfiles));
-        RList dimNames = originalVector.getDimNames(attrProfiles);
+        RLogicalVector result = RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR, getDimsNode.getDimensions(originalVector), getNamesNode.getNames(originalVector));
+        RList dimNames = getDimNamesNode.getDimNames(originalVector);
         if (nullDimNamesProfile.profile(dimNames != null)) {
-            result.setDimNames(dimNames);
+            setDimNamesNode.setDimNames(result, dimNames);
         }
         return result;
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
index 2dad6301ea6eca72e44a2a179ab7ffb2d0f5c549..8d3ca0f3b4ef3d2c4cfe9763960769dca7ff01df 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LaFunctions.java
@@ -28,9 +28,16 @@ import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+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.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
@@ -95,8 +102,8 @@ public class LaFunctions {
         private final ConditionProfile hasComplexValues = ConditionProfile.createBinaryProfile();
 
         @Specialization
-        protected Object doRg(RDoubleVector matrix, boolean onlyValues) {
-            int[] dims = matrix.getDimensions();
+        protected Object doRg(RDoubleVector matrix, boolean onlyValues, @Cached("create()") GetDimAttributeNode getDimsNode) {
+            int[] dims = getDimsNode.getDimensions(matrix);
             // copy array component of matrix as Lapack destroys it
             int n = dims[0];
             double[] a = matrix.getDataCopy();
@@ -189,8 +196,8 @@ public class LaFunctions {
     @RBuiltin(name = "La_rs", kind = INTERNAL, parameterNames = {"matrix", "onlyValues"}, behavior = PURE)
     public abstract static class Rs extends RsgAdapter {
         @Specialization
-        protected Object doRs(RDoubleVector matrix, boolean onlyValues) {
-            int[] dims = matrix.getDimensions();
+        protected Object doRs(RDoubleVector matrix, boolean onlyValues, @Cached("create()") GetDimAttributeNode getDimsNode) {
+            int[] dims = getDimsNode.getDimensions(matrix);
             int n = dims[0];
             char jobv = onlyValues ? 'N' : 'V';
             char uplo = 'L';
@@ -256,13 +263,15 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RList doQr(RAbstractDoubleVector aIn) {
+        protected RList doQr(RAbstractDoubleVector aIn,
+                        @Cached("create()") GetDimAttributeNode getDimsNode,
+                        @Cached("create()") SetDimAttributeNode setDimsNode) {
             // This implementation is sufficient for B25 matcal-5.
             if (!(aIn instanceof RDoubleVector)) {
                 RError.nyi(this, "non-real vectors not supported (yet)");
             }
             RDoubleVector daIn = (RDoubleVector) aIn;
-            int[] dims = daIn.getDimensions();
+            int[] dims = getDimsNode.getDimensions(daIn);
             // copy array component of matrix as Lapack destroys it
             int n = dims[0];
             int m = dims[1];
@@ -287,7 +296,7 @@ public class LaFunctions {
             // TODO check complete
             RDoubleVector ra = RDataFactory.createDoubleVector(a, RDataFactory.COMPLETE_VECTOR);
             // TODO check pivot
-            ra.setDimensions(dims);
+            setDimsNode.setDimensions(ra, dims);
             data[0] = ra;
             data[1] = m < n ? m : n;
             data[2] = RDataFactory.createDoubleVector(tau, RDataFactory.COMPLETE_VECTOR);
@@ -311,7 +320,9 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RDoubleVector doQrCoefReal(RList qIn, RDoubleVector bIn) {
+        protected RDoubleVector doQrCoefReal(RList qIn, RDoubleVector bIn,
+                        @Cached("create()") GetDimAttributeNode getBDimsNode,
+                        @Cached("create()") GetDimAttributeNode getQDimsNode) {
             // If bIn was coerced this extra copy is unnecessary
             RDoubleVector b = (RDoubleVector) bIn.copy();
 
@@ -320,8 +331,8 @@ public class LaFunctions {
             RDoubleVector tau = (RDoubleVector) qIn.getDataAt(2);
             int k = tau.getLength();
 
-            int[] bDims = bIn.getDimensions();
-            int[] qrDims = qr.getDimensions();
+            int[] bDims = getBDimsNode.getDimensions(bIn);
+            int[] qrDims = getQDimsNode.getDimensions(qr);
             int n = qrDims[0];
             if (bDims[0] != n) {
                 errorProfile.enter();
@@ -368,6 +379,8 @@ public class LaFunctions {
         private final ConditionProfile doUseLog = ConditionProfile.createBinaryProfile();
         private final NACheck naCheck = NACheck.create();
 
+        @Child private SetFixedAttributeNode setLogAttrNode = SetFixedAttributeNode.create("logarithm");
+
         @Override
         protected void createCasts(CastBuilder casts) {
             //@formatter:off
@@ -384,9 +397,10 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RList doDetGeReal(RDoubleVector aIn, boolean useLog) {
+        protected RList doDetGeReal(RDoubleVector aIn, boolean useLog,
+                        @Cached("create()") GetDimAttributeNode getDimsNode) {
             RDoubleVector a = (RDoubleVector) aIn.copy();
-            int[] aDims = aIn.getDimensions();
+            int[] aDims = getDimsNode.getDimensions(aIn);
             int n = aDims[0];
             int[] ipiv = new int[n];
             double modulus = 0;
@@ -437,7 +451,7 @@ public class LaFunctions {
                 }
             }
             RDoubleVector modulusVec = RDataFactory.createDoubleVectorFromScalar(modulus);
-            modulusVec.setAttr("logarithm", RRuntime.asLogical(useLog));
+            setLogAttrNode.execute(modulusVec, RRuntime.asLogical(useLog));
             RList result = RDataFactory.createList(new Object[]{modulusVec, sign}, NAMES_VECTOR);
             RVector.setVectorClassAttr(result, DET_CLASS);
             return result;
@@ -450,6 +464,9 @@ public class LaFunctions {
         private final BranchProfile errorProfile = BranchProfile.create();
         private final ConditionProfile noPivot = ConditionProfile.createBinaryProfile();
 
+        @Child private SetFixedAttributeNode setPivotAttrNode = SetFixedAttributeNode.create("pivot");
+        @Child private SetFixedAttributeNode setRankAttrNode = SetFixedAttributeNode.create("rank");
+
         @Override
         protected void createCasts(CastBuilder casts) {
             //@formatter:off
@@ -469,9 +486,12 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RDoubleVector doDetGeReal(RDoubleVector aIn, boolean piv, double tol) {
+        protected RDoubleVector doDetGeReal(RDoubleVector aIn, boolean piv, double tol,
+                        @Cached("create()") GetDimAttributeNode getDimsNode,
+                        @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                        @Cached("create()") GetDimNamesAttributeNode getDimNamesNode) {
             RDoubleVector a = (RDoubleVector) aIn.copy();
-            int[] aDims = aIn.getDimensions();
+            int[] aDims = getDimsNode.getDimensions(aIn);
             int n = aDims[0];
             int m = aDims[1];
             double[] aData = a.getDataWithoutCopying();
@@ -499,16 +519,16 @@ public class LaFunctions {
                     // TODO informative error message (aka GnuR)
                     throw RError.error(this, RError.Message.LAPACK_ERROR, info, "dpotrf");
                 }
-                a.setAttr("pivot", RRuntime.asLogical(piv));
-                a.setAttr("rank", rank[0]);
-                RList dn = a.getDimNames();
+                setPivotAttrNode.execute(a, RRuntime.asLogical(piv));
+                setRankAttrNode.execute(a, rank[0]);
+                RList dn = getDimNamesNode.getDimNames(a);
                 if (dn != null && dn.getDataAt(0) != null) {
                     Object[] dn2 = new Object[m];
                     // need to pivot the colnames
                     for (int i = 0; i < m; i++) {
                         dn2[i] = dn.getDataAt(ipiv[i] - 1);
                     }
-                    a.setDimNames(RDataFactory.createList(dn2));
+                    setDimNamesNode.setDimNames(a, RDataFactory.createList(dn2));
                 }
             }
             return a;
@@ -540,8 +560,15 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RDoubleVector laSolve(RAbstractVector a, RDoubleVector bin, double tol) {
-            int[] aDims = a.getDimensions();
+        protected RDoubleVector laSolve(RAbstractVector a, RDoubleVector bin, double tol,
+                        @Cached("create()") GetDimAttributeNode getADimsNode,
+                        @Cached("create()") GetDimAttributeNode getBinDimsNode,
+                        @Cached("create()") SetDimAttributeNode setBDimsNode,
+                        @Cached("create()") SetDimNamesAttributeNode setBDimNamesNode,
+                        @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                        @Cached("create()") GetDimNamesAttributeNode getBinDimNamesNode,
+                        @Cached("create()") SetNamesAttributeNode setNamesNode) {
+            int[] aDims = getADimsNode.getDimensions(a);
             int n = aDims[0];
             if (n == 0) {
                 throw RError.error(this, RError.Message.GENERIC, "'a' is 0-diml");
@@ -550,12 +577,12 @@ public class LaFunctions {
             if (n2 != n) {
                 throw RError.error(this, RError.Message.MUST_BE_SQUARE, "a", n, n2);
             }
-            RList aDn = a.getDimNames(attrProfiles);
+            RList aDn = getADimNamesNode.getDimNames(a);
             int p;
             double[] bData;
             RDoubleVector b;
             if (bin.isMatrix()) {
-                int[] bDims = bin.getDimensions();
+                int[] bDims = getBinDimsNode.getDimensions(bin);
                 p = bDims[1];
                 if (p == 0) {
                     throw RError.error(this, RError.Message.GENERIC, "no right-hand side in 'b'");
@@ -566,8 +593,8 @@ public class LaFunctions {
                 }
                 bData = new double[n * p];
                 b = RDataFactory.createDoubleVector(bData, RDataFactory.COMPLETE_VECTOR);
-                b.setDimensions(new int[]{n, p});
-                RList binDn = bin.getDimNames();
+                setBDimsNode.setDimensions(b, new int[]{n, p});
+                RList binDn = getBinDimNamesNode.getDimNames(bin);
                 // This is somewhat odd, but Matrix relies on dropping NULL dimnames
                 if (aDn != null || binDn != null) {
                     // rownames(ans) = colnames(A), colnames(ans) = colnames(Bin)
@@ -579,7 +606,7 @@ public class LaFunctions {
                         bDnData[1] = binDn.getDataAt(1);
                     }
                     if (bDnData[0] != null || bDnData[1] != null) {
-                        b.setDimNames(RDataFactory.createList(bDnData));
+                        setBDimNamesNode.setDimNames(b, RDataFactory.createList(bDnData));
                     }
                 }
             } else {
@@ -590,7 +617,7 @@ public class LaFunctions {
                 bData = new double[n];
                 b = RDataFactory.createDoubleVector(bData, RDataFactory.COMPLETE_VECTOR);
                 if (aDn != null) {
-                    b.setNames(RDataFactory.createStringVector((String) aDn.getDataAt(1)));
+                    setNamesNode.setNames(b, RDataFactory.createStringVector((String) aDn.getDataAt(1)));
                 }
             }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
index a75fdbb515e1a6c46e2d4a92aedaf3d6a5d9a70a..54ecf0b6dc9db4eaf3fcfd74062c55e6372bb18c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Lapply.java
@@ -33,6 +33,7 @@ import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNodeGen;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.LapplyNodeGen.LapplyInternalNodeGen;
@@ -45,7 +46,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -69,8 +69,6 @@ public abstract class Lapply extends RBuiltinNode {
 
     private static final Source CALL_SOURCE = RSource.fromTextInternal("FUN(X[[i]], ...)", RSource.Internal.LAPPLY);
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Child private LapplyInternalNode lapply = LapplyInternalNodeGen.create();
 
     @Override
@@ -83,10 +81,10 @@ public abstract class Lapply extends RBuiltinNode {
     }
 
     @Specialization
-    protected Object lapply(VirtualFrame frame, RAbstractVector vec, RFunction fun) {
+    protected Object lapply(VirtualFrame frame, RAbstractVector vec, RFunction fun, @Cached("create()") GetNamesAttributeNode getNamesNode) {
         Object[] result = lapply.execute(frame, vec, fun);
         // set here else it gets overridden by the iterator evaluation
-        return RDataFactory.createList(result, vec.getNames(attrProfiles));
+        return RDataFactory.createList(result, getNamesNode.getNames(vec));
     }
 
     private static final class ExtractElementInternal extends RSourceSectionNode implements RSyntaxCall {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
index 8532659c5be0afa030c0bac1a55a0e52f9406904..457eca9da958eace8975e6ec36c6fb2cd25d90ca 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/LogFunctions.java
@@ -32,6 +32,8 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
@@ -39,7 +41,6 @@ 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.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
@@ -80,7 +81,8 @@ public class LogFunctions {
         @Specialization
         protected RDoubleVector log(RAbstractIntVector vector, double base,
                         @Cached("create()") CopyOfRegAttributesNode copyAttrsNode,
-                        @Cached("create()") RAttributeProfiles attrProfiles) {
+                        @Cached("create()") GetNamesAttributeNode getNamesNode,
+                        @Cached("create()") GetDimAttributeNode getDimsNode) {
             double[] resultVector = new double[vector.getLength()];
             for (int i = 0; i < vector.getLength(); i++) {
                 int inputValue = vector.getDataAt(i);
@@ -90,13 +92,14 @@ public class LogFunctions {
                 }
                 resultVector[i] = result;
             }
-            return createResult(vector, resultVector, base, copyAttrsNode, attrProfiles);
+            return createResult(vector, resultVector, base, copyAttrsNode, getNamesNode, getDimsNode);
         }
 
         @Specialization
         protected RDoubleVector log(RAbstractDoubleVector vector, double base,
                         @Cached("create()") CopyOfRegAttributesNode copyAttrsNode,
-                        @Cached("create()") RAttributeProfiles attrProfiles) {
+                        @Cached("create()") GetNamesAttributeNode getNamesNode,
+                        @Cached("create()") GetDimAttributeNode getDimsNode) {
             double[] doubleVector = new double[vector.getLength()];
             for (int i = 0; i < vector.getLength(); i++) {
                 double value = vector.getDataAt(i);
@@ -105,7 +108,7 @@ public class LogFunctions {
                 }
                 doubleVector[i] = value;
             }
-            return createResult(vector, doubleVector, base, copyAttrsNode, attrProfiles);
+            return createResult(vector, doubleVector, base, copyAttrsNode, getNamesNode, getDimsNode);
         }
 
         private double logb(double x, double base) {
@@ -121,8 +124,9 @@ public class LogFunctions {
             return Math.log(x) / Math.log(base);
         }
 
-        private static RDoubleVector createResult(RAbstractVector source, double[] resultData, double base, CopyOfRegAttributesNode copyAttrsNode, RAttributeProfiles attrProfiles) {
-            RDoubleVector result = RDataFactory.createDoubleVector(resultData, source.isComplete() && !RRuntime.isNA(base), source.getDimensions(), source.getNames(attrProfiles));
+        private static RDoubleVector createResult(RAbstractVector source, double[] resultData, double base, CopyOfRegAttributesNode copyAttrsNode, GetNamesAttributeNode getNamesNode,
+                        GetDimAttributeNode getDimsNode) {
+            RDoubleVector result = RDataFactory.createDoubleVector(resultData, source.isComplete() && !RRuntime.isNA(base), getDimsNode.getDimensions(source), getNamesNode.getNames(source));
             copyAttrsNode.execute(source, result);
             return result;
         }
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
index f363a88b079d661e0154ec885754984e446561d8..372547fa0f8364b32aa5a51ea702a98dcaeaae62 100644
--- 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
@@ -37,6 +37,7 @@ import com.oracle.truffle.r.nodes.access.FrameSlotNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode;
 import com.oracle.truffle.r.nodes.access.WriteVariableNode.Mode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.MapplyNodeGen.MapplyInternalNodeGen;
@@ -49,7 +50,6 @@ import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLogicalVector;
@@ -124,7 +124,7 @@ public abstract class Mapply extends RBuiltinNode {
             }
         }
 
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
         public abstract Object[] execute(VirtualFrame frame, RAbstractListVector dots, RFunction function, RAbstractListVector additionalArguments);
 
@@ -215,13 +215,13 @@ public abstract class Mapply extends RBuiltinNode {
             }
             Object[] values = new Object[dotsLength + moreArgsLength];
             String[] names = new String[dotsLength + moreArgsLength];
-            RStringVector dotsNames = dots.getNames(attrProfiles);
+            RStringVector dotsNames = getNames.getNames(dots);
             if (dotsNames != null) {
                 for (int listIndex = 0; listIndex < dotsLength; listIndex++) {
                     names[listIndex] = dotsNames.getDataAt(listIndex).isEmpty() ? null : dotsNames.getDataAt(listIndex);
                 }
             }
-            RStringVector moreArgsNames = moreArgs.getNames(attrProfiles);
+            RStringVector moreArgsNames = getNames.getNames(moreArgs);
             for (int listIndex = dotsLength; listIndex < dotsLength + moreArgsLength; listIndex++) {
                 values[listIndex] = moreArgs.getDataAt(listIndex - dotsLength);
                 names[listIndex] = moreArgsNames == null ? null : (moreArgsNames.getDataAt(listIndex - dotsLength).isEmpty() ? null : moreArgsNames.getDataAt(listIndex - dotsLength));
@@ -260,11 +260,11 @@ public abstract class Mapply extends RBuiltinNode {
         protected ElementNode[] createElementNodeArray(RAbstractListVector dots, RAbstractListVector moreArgs) {
             int length = dots.getLength() + moreArgs.getLength();
             ElementNode[] elementNodes = new ElementNode[length];
-            RStringVector dotsNames = dots.getNames(attrProfiles);
+            RStringVector dotsNames = getNames.getNames(dots);
             for (int i = 0; i < dots.getLength(); i++) {
                 elementNodes[i] = insert(new ElementNode(VECTOR_ELEMENT_PREFIX + (i + 1), dotsNames == null ? null : (dotsNames.getDataAt(i).isEmpty() ? null : dotsNames.getDataAt(i))));
             }
-            RStringVector moreArgsNames = moreArgs.getNames(attrProfiles);
+            RStringVector moreArgsNames = getNames.getNames(moreArgs);
             for (int i = dots.getLength(); i < dots.getLength() + moreArgs.getLength(); i++) {
                 elementNodes[i] = insert(new ElementNode(VECTOR_ELEMENT_PREFIX + (i + 1),
                                 moreArgsNames == null ? null : moreArgsNames.getDataAt(i - dots.getLength()).isEmpty() ? null : moreArgsNames.getDataAt(i - dots.getLength())));
@@ -289,14 +289,17 @@ public abstract class Mapply extends RBuiltinNode {
         }
 
         protected boolean sameNames(RAbstractListVector list, RAbstractListVector cachedList) {
-            if (list.getNames(attrProfiles) == null && cachedList.getNames(attrProfiles) == null) {
+            RStringVector listNames = getNames.getNames(list);
+            RStringVector cachedListNames = getNames.getNames(cachedList);
+            if (listNames == null && cachedListNames == null) {
                 return true;
-            } else if (list.getNames(attrProfiles) == null || cachedList.getNames(attrProfiles) == null) {
+            } else if (listNames == null || cachedListNames == null) {
                 return false;
             } else {
+
                 for (int i = 0; i < list.getLength(); i++) {
-                    String name = list.getNames(attrProfiles).getDataAt(i);
-                    String cachedName = cachedList.getNames(attrProfiles).getDataAt(i);
+                    String name = listNames.getDataAt(i);
+                    String cachedName = cachedListNames.getDataAt(i);
 
                     if (name == cachedName) {
                         continue;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
index c7a5ea146e47895dba8a926822b61db083c8c696..7bee0fa552fc045332ea15babf8bdaa0daca15f6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/MatMult.java
@@ -34,12 +34,15 @@ import com.oracle.truffle.api.dsl.Specialization;
 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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.binary.BinaryMapArithmeticFunctionNode;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -71,10 +74,12 @@ public abstract class MatMult extends RBuiltinNode {
     private final ConditionProfile notOneRow = ConditionProfile.createBinaryProfile();
     private final ConditionProfile notOneColumn = ConditionProfile.createBinaryProfile();
 
-    private final RAttributeProfiles aDimAttributeProfile = RAttributeProfiles.create();
-    private final RAttributeProfiles bDimAttributeProfile = RAttributeProfiles.create();
     private final ConditionProfile noDimAttributes = ConditionProfile.createBinaryProfile();
 
+    @Child protected GetDimAttributeNode getADimsNode = GetDimAttributeNode.create();
+    @Child protected GetDimAttributeNode getBDimsNode = GetDimAttributeNode.create();
+    @Child protected SetDimAttributeNode setDimsNode = SetDimAttributeNode.create();
+
     protected abstract Object executeObject(Object a, Object b);
 
     private final NACheck na;
@@ -88,24 +93,26 @@ public abstract class MatMult extends RBuiltinNode {
         return MatMultNodeGen.create(true);
     }
 
-    @Specialization(guards = "bothZeroDim(a, b)")
+    @Specialization(guards = "bothZeroDim(a, b, getADimsNode, getBDimsNode)")
     protected RDoubleVector both0Dim(RAbstractDoubleVector a, RAbstractDoubleVector b) {
-        int r = b.getDimensions()[1];
-        int c = a.getDimensions()[0];
+        int r = getBDimsNode.getDimensions(b)[1];
+        int c = getADimsNode.getDimensions(a)[0];
         RDoubleVector result = RDataFactory.createDoubleVector(r * c);
-        result.setDimensions(new int[]{r, c});
+        setDimsNode.setDimensions(result, new int[]{r, c});
         return result;
     }
 
-    @Specialization(guards = "hasZeroDim(a)")
+    @Specialization(guards = "hasZeroDim(a, getADimsNode)")
     protected RAbstractVector left0Dim(RAbstractVector a, RAbstractVector b) {
-        int[] dim = a.getDimensions()[0] == 0 ? new int[]{0, b.getDimensions()[1]} : new int[]{b.getDimensions()[0], 0};
+        int[] aDim = getADimsNode.getDimensions(a);
+        int[] dim = aDim[0] == 0 ? new int[]{0, getBDimsNode.getDimensions(b)[1]} : new int[]{getBDimsNode.getDimensions(b)[0], 0};
         return a.copyWithNewDimensions(dim);
     }
 
-    @Specialization(guards = "hasZeroDim(b)")
+    @Specialization(guards = "hasZeroDim(b, getBDimsNode)")
     protected RAbstractVector right0Dim(RAbstractVector a, RAbstractVector b) {
-        int[] dim = b.getDimensions()[0] == 0 ? new int[]{0, a.getDimensions()[1]} : new int[]{a.getDimensions()[0], 0};
+        int[] bDim = getBDimsNode.getDimensions(b);
+        int[] dim = bDim[0] == 0 ? new int[]{0, getADimsNode.getDimensions(a)[1]} : new int[]{getADimsNode.getDimensions(a)[0], 0};
         return b.copyWithNewDimensions(dim);
     }
 
@@ -133,8 +140,9 @@ public abstract class MatMult extends RBuiltinNode {
     private final BranchProfile incompleteProfile = BranchProfile.create();
     @CompilationFinal private boolean seenLargeMatrix;
 
-    private RDoubleVector doubleMatrixMultiply(RAbstractDoubleVector a, RAbstractDoubleVector b, int aRows, int aCols, int bRows, int bCols) {
-        return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols, 1, aRows, 1, bRows, false);
+    private RDoubleVector doubleMatrixMultiply(RAbstractDoubleVector a, RAbstractDoubleVector b, int aRows, int aCols, int bRows, int bCols, SetDimNamesAttributeNode setDimNamesNode,
+                    GetDimNamesAttributeNode getADimNamesNode, GetDimNamesAttributeNode getBDimNamesNode) {
+        return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols, 1, aRows, 1, bRows, false, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
     /**
@@ -155,7 +163,7 @@ public abstract class MatMult extends RBuiltinNode {
      * @return the result vector
      */
     public RDoubleVector doubleMatrixMultiply(RAbstractDoubleVector a, RAbstractDoubleVector b, int aRows, int aCols, int bRows, int bCols, int aRowStride, int aColStride, int bRowStride,
-                    int bColStride, boolean mirrored) {
+                    int bColStride, boolean mirrored, SetDimNamesAttributeNode setDimNamesNode, GetDimNamesAttributeNode getADimNamesNode, GetDimNamesAttributeNode getBDimNamesNode) {
         if (aCols != bRows) {
             errorProfile.enter();
             throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
@@ -207,8 +215,8 @@ public abstract class MatMult extends RBuiltinNode {
         }
 
         RDoubleVector resultVec = RDataFactory.createDoubleVector(result, complete, new int[]{aRows, bCols});
-        RList aDimNames = a.getDimNames(aDimAttributeProfile);
-        RList bDimNames = b.getDimNames(bDimAttributeProfile);
+        RList aDimNames = getADimNamesNode.getDimNames(a);
+        RList bDimNames = getBDimNamesNode.getDimNames(b);
         if (!promoteDimNames || noDimAttributes.profile(aDimNames == null && bDimNames == null)) {
             return resultVec;
         }
@@ -220,7 +228,7 @@ public abstract class MatMult extends RBuiltinNode {
         if (bDimNames != null && bDimNames.getLength() > 1) {
             newDimsNames[1] = bDimNames.getDataAt(1);
         }
-        resultVec.setDimNames(RDataFactory.createList(newDimsNames));
+        setDimNamesNode.setDimNames(resultVec, RDataFactory.createList(newDimsNames));
         return resultVec;
     }
 
@@ -265,15 +273,19 @@ public abstract class MatMult extends RBuiltinNode {
     protected RDoubleVector multiply(RAbstractDoubleVector a, RAbstractDoubleVector b,
                     @Cached("createBinaryProfile()") ConditionProfile aIsMatrix,
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix,
-                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
+                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
         if (aIsMatrix.profile(a.isMatrix())) {
             if (bIsMatrix.profile(b.isMatrix())) {
-                int[] aDimensions = a.getDimensions();
-                int[] bDimensions = b.getDimensions();
-                return doubleMatrixMultiply(a, b, aDimensions[0], aDimensions[1], bDimensions[0], bDimensions[1]);
+                int[] aDimensions = getADimsNode.getDimensions(a);
+                int[] bDimensions = getBDimsNode.getDimensions(b);
+                return doubleMatrixMultiply(a, b, aDimensions[0], aDimensions[1], bDimensions[0], bDimensions[1], setDimNamesNode, getADimNamesNode, getBDimNamesNode);
             } else {
-                int aRows = a.getDimensions()[0];
-                int aCols = a.getDimensions()[1];
+                int[] aDim = getADimsNode.getDimensions(a);
+                int aRows = aDim[0];
+                int aCols = aDim[1];
                 int bRows;
                 int bCols;
                 if (lengthEquals.profile(aCols == b.getLength())) {
@@ -283,12 +295,13 @@ public abstract class MatMult extends RBuiltinNode {
                     bRows = 1;
                     bCols = b.getLength();
                 }
-                return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols);
+                return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
             }
         } else {
             if (bIsMatrix.profile(b.isMatrix())) {
-                int bRows = b.getDimensions()[0];
-                int bCols = b.getDimensions()[1];
+                int[] bDim = getBDimsNode.getDimensions(b);
+                int bRows = bDim[0];
+                int bCols = bDim[1];
                 int aRows;
                 int aCols;
                 if (lengthEquals.profile(bRows == a.getLength())) {
@@ -298,7 +311,7 @@ public abstract class MatMult extends RBuiltinNode {
                     aRows = a.getLength();
                     aCols = 1;
                 }
-                return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols);
+                return doubleMatrixMultiply(a, b, aRows, aCols, bRows, bCols, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
             } else {
                 if (a.getLength() != b.getLength()) {
                     errorProfile.enter();
@@ -328,14 +341,16 @@ public abstract class MatMult extends RBuiltinNode {
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix) {
         if (aIsMatrix.profile(a.isMatrix())) {
             if (bIsMatrix.profile(b.isMatrix())) {
-                final int aCols = a.getDimensions()[1];
-                final int bRows = b.getDimensions()[0];
+                int[] aDim = getADimsNode.getDimensions(a);
+                int[] bDim = getBDimsNode.getDimensions(b);
+                final int aCols = aDim[1];
+                final int bRows = bDim[0];
                 if (aCols != bRows) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
                 }
-                final int aRows = a.getDimensions()[0];
-                final int bCols = b.getDimensions()[1];
+                final int aRows = aDim[0];
+                final int bCols = bDim[1];
                 double[] result = new double[(aRows * bCols) << 1];
                 na.enable(a);
                 na.enable(b);
@@ -353,8 +368,9 @@ public abstract class MatMult extends RBuiltinNode {
                 }
                 return RDataFactory.createComplexVector(result, na.neverSeenNA(), new int[]{aRows, bCols});
             } else {
-                final int aCols = a.getDimensions()[1];
-                final int aRows = a.getDimensions()[0];
+                int[] aDim = getADimsNode.getDimensions(a);
+                final int aCols = aDim[1];
+                final int aRows = aDim[0];
                 if (aCols != 1 && aCols != b.getLength()) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
@@ -388,8 +404,9 @@ public abstract class MatMult extends RBuiltinNode {
             }
         } else {
             if (bIsMatrix.profile(b.isMatrix())) {
-                final int bRows = b.getDimensions()[0];
-                final int bCols = b.getDimensions()[1];
+                int[] bDim = getBDimsNode.getDimensions(b);
+                final int bRows = bDim[0];
+                final int bCols = bDim[1];
                 if (bRows != 1 && bRows != a.getLength()) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
@@ -445,14 +462,16 @@ public abstract class MatMult extends RBuiltinNode {
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix) {
         if (aIsMatrix.profile(a.isMatrix())) {
             if (bIsMatrix.profile(b.isMatrix())) {
-                final int aCols = a.getDimensions()[1];
-                final int bRows = b.getDimensions()[0];
+                int[] aDim = getADimsNode.getDimensions(a);
+                int[] bDim = getBDimsNode.getDimensions(b);
+                final int aCols = aDim[1];
+                final int bRows = bDim[0];
                 if (aCols != bRows) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
                 }
-                final int aRows = a.getDimensions()[0];
-                final int bCols = b.getDimensions()[1];
+                final int aRows = aDim[0];
+                final int bCols = bDim[1];
                 int[] result = new int[aRows * bCols];
                 na.enable(a);
                 na.enable(b);
@@ -468,8 +487,9 @@ public abstract class MatMult extends RBuiltinNode {
                 }
                 return RDataFactory.createIntVector(result, na.neverSeenNA(), new int[]{aRows, bCols});
             } else {
-                final int aCols = a.getDimensions()[1];
-                final int aRows = a.getDimensions()[0];
+                int[] aDim = getADimsNode.getDimensions(a);
+                final int aCols = aDim[1];
+                final int aRows = aDim[0];
                 if (aCols != 1 && aCols != b.getLength()) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
@@ -500,9 +520,10 @@ public abstract class MatMult extends RBuiltinNode {
                 }
             }
         } else {
+            int[] bDim = getBDimsNode.getDimensions(b);
             if (bIsMatrix.profile(b.isMatrix())) {
-                final int bCols = b.getDimensions()[1];
-                final int bRows = b.getDimensions()[0];
+                final int bCols = bDim[1];
+                final int bRows = bDim[0];
                 if (bRows != 1 && bRows != a.getLength()) {
                     errorProfile.enter();
                     throw RError.error(this, RError.Message.NON_CONFORMABLE_ARGS);
@@ -622,32 +643,44 @@ public abstract class MatMult extends RBuiltinNode {
     protected RDoubleVector multiply(RAbstractIntVector a, RAbstractDoubleVector b,
                     @Cached("createBinaryProfile()") ConditionProfile aIsMatrix,
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix,
-                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
-        return multiply(RClosures.createIntToDoubleVector(a), b, aIsMatrix, bIsMatrix, lengthEquals);
+                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        return multiply(RClosures.createIntToDoubleVector(a), b, aIsMatrix, bIsMatrix, lengthEquals, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
     @Specialization
     protected RDoubleVector multiply(RAbstractDoubleVector a, RAbstractIntVector b,
                     @Cached("createBinaryProfile()") ConditionProfile aIsMatrix,
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix,
-                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
-        return multiply(a, RClosures.createIntToDoubleVector(b), aIsMatrix, bIsMatrix, lengthEquals);
+                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        return multiply(a, RClosures.createIntToDoubleVector(b), aIsMatrix, bIsMatrix, lengthEquals, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
     @Specialization
     protected RDoubleVector multiply(RAbstractLogicalVector a, RAbstractDoubleVector b,
                     @Cached("createBinaryProfile()") ConditionProfile aIsMatrix,
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix,
-                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
-        return multiply(RClosures.createLogicalToDoubleVector(a), b, aIsMatrix, bIsMatrix, lengthEquals);
+                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        return multiply(RClosures.createLogicalToDoubleVector(a), b, aIsMatrix, bIsMatrix, lengthEquals, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
     @Specialization
     protected RDoubleVector multiply(RAbstractDoubleVector a, RAbstractLogicalVector b,
                     @Cached("createBinaryProfile()") ConditionProfile aIsMatrix,
                     @Cached("createBinaryProfile()") ConditionProfile bIsMatrix,
-                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
-        return multiply(a, RClosures.createLogicalToDoubleVector(b), aIsMatrix, bIsMatrix, lengthEquals);
+                    @Cached("createBinaryProfile()") ConditionProfile lengthEquals,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getADimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getBDimNamesNode) {
+        return multiply(a, RClosures.createLogicalToDoubleVector(b), aIsMatrix, bIsMatrix, lengthEquals, setDimNamesNode, getADimNamesNode, getBDimNamesNode);
     }
 
     // errors
@@ -660,15 +693,16 @@ public abstract class MatMult extends RBuiltinNode {
 
     // guards
 
-    protected static boolean bothZeroDim(RAbstractVector a, RAbstractVector b) {
-        return hasZeroDim(a) && hasZeroDim(b);
+    protected static boolean bothZeroDim(RAbstractVector a, RAbstractVector b, GetDimAttributeNode getADimsNode, GetDimAttributeNode getBDimsNode) {
+        return hasZeroDim(a, getADimsNode) && hasZeroDim(b, getBDimsNode);
     }
 
-    protected static boolean hasZeroDim(RAbstractVector v) {
-        if (!v.hasDimensions()) {
+    protected static boolean hasZeroDim(RAbstractVector v, GetDimAttributeNode getDimsNode) {
+        int[] dims = getDimsNode.getDimensions(v);
+        if (dims == null || dims.length == 0) {
             return false;
         }
-        for (int d : v.getDimensions()) {
+        for (int d : dims) {
             if (d == 0) {
                 return true;
             }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
index bec8a180fab423d0887b7139cc7907743180d4a5..0895f7591631e6d7ef4c4630ecd35884c4923457 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Matrix.java
@@ -28,8 +28,10 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
 
 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.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -75,7 +77,8 @@ public abstract class Matrix extends RBuiltinNode {
     }
 
     @Specialization
-    protected RAbstractVector matrix(RAbstractVector data, int nrow, int ncol, boolean byrow, Object dimnames, boolean missingNr, boolean missingNc) {
+    protected RAbstractVector matrix(RAbstractVector data, int nrow, int ncol, boolean byrow, Object dimnames, boolean missingNr, boolean missingNc,
+                    @Cached("create()") SetDimAttributeNode setDimNode) {
         int[] dim;
         if (byrowProfile.profile(byrow)) {
             dim = computeDimByRow(data.getLength(), nrow, ncol, missingNr, missingNc);
@@ -99,7 +102,7 @@ public abstract class Matrix extends RBuiltinNode {
                 }
             } else {
                 res = data.createEmptySameType(0, RDataFactory.COMPLETE_VECTOR);
-                res.setDimensions(dim);
+                setDimNode.setDimensions(res, dim);
             }
         } else {
             res = data.copyResizedWithDimensions(dim, false);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
index 1f0a0a0deb44b0cb65f736923ed39858c624ab8f..c2faec1d69a4fca54a7103a68e8e3552c7ac485b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
@@ -33,11 +33,14 @@ 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.LoopConditionProfile;
+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.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -67,8 +70,11 @@ public abstract class NChar extends RBuiltinNode {
     @Specialization
     protected RIntVector ncharInt(RAbstractIntVector vector, String type, boolean allowNA, boolean keepNA,
                     @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
-                    @Cached("create()") RAttributeProfiles attrProfiles,
-                    @Cached("createBinaryProfile()") ConditionProfile nullDimNamesProfile) {
+                    @Cached("createBinaryProfile()") ConditionProfile nullDimNamesProfile,
+                    @Cached("create()") GetDimAttributeNode getDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         int len = vector.getLength();
         int[] result = new int[len];
         loopProfile.profileCounted(len);
@@ -80,10 +86,10 @@ public abstract class NChar extends RBuiltinNode {
                 result[i] = (int) (Math.log10(x) + 1); // not the fastest one
             }
         }
-        RIntVector resultVector = RDataFactory.createIntVector(result, true, vector.getDimensions(), vector.getNames(attrProfiles));
-        RList dimNames = vector.getDimNames(attrProfiles);
+        RIntVector resultVector = RDataFactory.createIntVector(result, true, getDimNode.getDimensions(vector), getNamesNode.getNames(vector));
+        RList dimNames = getDimNamesNode.getDimNames(vector);
         if (nullDimNamesProfile.profile(dimNames != null)) {
-            resultVector.setDimNames(dimNames);
+            setDimNamesNode.setDimNames(resultVector, dimNames);
         }
         return resultVector;
     }
@@ -92,18 +98,21 @@ public abstract class NChar extends RBuiltinNode {
     @Specialization
     protected RIntVector nchar(RAbstractStringVector vector, String type, boolean allowNA, boolean keepNA,
                     @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
-                    @Cached("create()") RAttributeProfiles attrProfiles,
-                    @Cached("createBinaryProfile()") ConditionProfile nullDimNamesProfile) {
+                    @Cached("createBinaryProfile()") ConditionProfile nullDimNamesProfile,
+                    @Cached("create()") GetDimAttributeNode getDimNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                    @Cached("create()") GetDimNamesAttributeNode getDimNamesNode,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         int len = vector.getLength();
         int[] result = new int[len];
         loopProfile.profileCounted(len);
         for (int i = 0; loopProfile.inject(i < len); i++) {
             result[i] = vector.getDataAt(i).length();
         }
-        RIntVector resultVector = RDataFactory.createIntVector(result, true, vector.getDimensions(), vector.getNames(attrProfiles));
-        RList dimNames = vector.getDimNames(attrProfiles);
+        RIntVector resultVector = RDataFactory.createIntVector(result, true, getDimNode.getDimensions(vector), getNamesNode.getNames(vector));
+        RList dimNames = getDimNamesNode.getDimNames(vector);
         if (nullDimNamesProfile.profile(dimNames != null)) {
-            resultVector.setDimNames(dimNames);
+            setDimNamesNode.setDimNames(resultVector, dimNames);
         }
         return resultVector;
     }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
index 91e6f763bcd36279b045cded3fa328d0a1cdf242..55c50f76361cc2080b1c84120370207135a03ece 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Names.java
@@ -30,10 +30,11 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
@@ -41,12 +42,13 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 public abstract class Names extends RBuiltinNode {
 
     private final ConditionProfile hasNames = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
     @Specialization
     protected Object getNames(RAbstractContainer container) {
-        if (hasNames.profile(container.getNames(attrProfiles) != null)) {
-            return container.getNames(attrProfiles);
+        RStringVector names = getNames.getNames(container);
+        if (hasNames.profile(names != null)) {
+            return names;
         } else {
             return RNull.instance;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
index f5487620db0fb63a21abd652f71710e0cf582d99..9486a8a9d9f6bde07fe95e17735384e70462fcb8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/OptionsFunctions.java
@@ -22,7 +22,7 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base;
 
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.*;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.MODIFIES_STATE;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_STATE;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
index 8ac93354d9a2fdb4f9223c82978ee91a8110bf19..07a3c5f15a5a4458d230723f3b594788c05983c8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Parse.java
@@ -35,6 +35,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
@@ -96,6 +97,10 @@ public abstract class Parse extends RBuiltinNode {
     @Child private CastStringNode castStringNode;
     @Child private CastToVectorNode castVectorNode;
 
+    @Child private SetFixedAttributeNode setSrcRefAttrNode = SetFixedAttributeNode.create("srcref");
+    @Child private SetFixedAttributeNode setWholeSrcRefAttrNode = SetFixedAttributeNode.create("wholeSrcref");
+    @Child private SetFixedAttributeNode setSrcFileAttrNode = SetFixedAttributeNode.create("srcfile");
+
     @Override
     protected void createCasts(CastBuilder casts) {
         // Note: string is captured by the R wrapper and transformed to a file, other types not
@@ -219,7 +224,7 @@ public abstract class Parse extends RBuiltinNode {
         }
     }
 
-    private static void addAttributes(RExpression exprs, Source source, REnvironment srcFile) {
+    private void addAttributes(RExpression exprs, Source source, REnvironment srcFile) {
         Object[] srcrefData = new Object[exprs.getLength()];
         for (int i = 0; i < srcrefData.length; i++) {
             Object data = exprs.getDataAt(i);
@@ -239,7 +244,7 @@ public abstract class Parse extends RBuiltinNode {
             }
 
         }
-        exprs.setAttr("srcref", RDataFactory.createList(srcrefData));
+        setSrcRefAttrNode.execute(exprs, RDataFactory.createList(srcrefData));
         int[] wholeSrcrefData = new int[8];
         int endOffset = source.getCode().length() - 1;
         wholeSrcrefData[0] = source.getLineNumber(0);
@@ -248,8 +253,8 @@ public abstract class Parse extends RBuiltinNode {
         wholeSrcrefData[6] = wholeSrcrefData[0];
         wholeSrcrefData[6] = wholeSrcrefData[3];
 
-        exprs.setAttr("wholeSrcref", RDataFactory.createIntVector(wholeSrcrefData, RDataFactory.COMPLETE_VECTOR));
-        exprs.setAttr("srcfile", srcFile);
+        setWholeSrcRefAttrNode.execute(exprs, RDataFactory.createIntVector(wholeSrcrefData, RDataFactory.COMPLETE_VECTOR));
+        setSrcFileAttrNode.execute(exprs, srcFile);
     }
 
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
index be182b91b23a5b1ef9147f6ae56359df1f2d456c..24496e3ae2d465157530b90cb0d3a2f76122ff08 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ProcTime.java
@@ -27,6 +27,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -44,6 +45,8 @@ public abstract class ProcTime extends RBuiltinNode {
 
     private static RStringVector RNAMES;
 
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
     @Specialization
     @TruffleBoundary
     protected RDoubleVector procTime() {
@@ -65,7 +68,8 @@ public abstract class ProcTime extends RBuiltinNode {
             RNAMES = RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR);
         }
         RDoubleVector result = RDataFactory.createDoubleVector(data, complete, RNAMES);
-        result.setClassAttr(PROC_TIME_CLASS);
+        setClassAttrNode.execute(result, PROC_TIME_CLASS);
+
         return result;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
index 743d510865d5285d571a2fc60fcc33c6765e69e3..e89c2645f46e5482347b24152fbff85318ef7c3a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Repeat.java
@@ -37,14 +37,14 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
-import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RMissing;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -83,7 +83,7 @@ public abstract class Repeat extends RBuiltinNode {
     private final BranchProfile errorBranch = BranchProfile.create();
     private final ConditionProfile oneTimeGiven = ConditionProfile.createBinaryProfile();
     private final ConditionProfile replicateOnce = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
     @Override
     public Object[] getDefaultParameterValues() {
@@ -106,7 +106,7 @@ public abstract class Repeat extends RBuiltinNode {
     }
 
     protected boolean hasNames(RAbstractVector x) {
-        return x.getNames(attrProfiles) != null;
+        return getNames.getNames(x) != null;
     }
 
     private RError invalidTimes() {
@@ -158,7 +158,7 @@ public abstract class Repeat extends RBuiltinNode {
             throw invalidTimes();
         }
         RAbstractVector input = handleEach(x, each);
-        RStringVector names = (RStringVector) handleEach(x.getNames(attrProfiles), each);
+        RStringVector names = (RStringVector) handleEach(getNames.getNames(x), each);
         RVector<?> r;
         if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
             names = (RStringVector) handleLengthOut(names, lengthOut, false);
@@ -168,7 +168,6 @@ public abstract class Repeat extends RBuiltinNode {
             r = handleTimes(input, times, false);
         }
         putNames.execute(initAttributes.execute(r), names);
-        r.setInternalNames(names);
         return r;
     }
 
@@ -179,14 +178,13 @@ public abstract class Repeat extends RBuiltinNode {
         RStringVector names;
         RVector<?> r;
         if (lengthOutOrTimes.profile(!RRuntime.isNA(lengthOut))) {
-            names = (RStringVector) handleLengthOut(x.getNames(attrProfiles), lengthOut, true);
+            names = (RStringVector) handleLengthOut(getNames.getNames(x), lengthOut, true);
             r = handleLengthOut(x, lengthOut, true);
         } else {
-            names = (RStringVector) handleTimes(x.getNames(attrProfiles), times, true);
+            names = (RStringVector) handleTimes(getNames.getNames(x), times, true);
             r = handleTimes(x, times, true);
         }
         putNames.execute(initAttributes.execute(r), names);
-        r.setInternalNames(names);
         return r;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
index e6bce9fd7f17f5cd0f3016bc0a853fa078a1aa93..8a5488c18d78944eeb4a906301b760a75846ef0b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Round.java
@@ -32,7 +32,6 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -55,7 +54,6 @@ public abstract class Round extends RBuiltinNode {
     @Child private RoundArithmetic roundOp = new RoundArithmetic();
 
     private final NACheck check = NACheck.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
     @Override
     public Object[] getDefaultParameterValues() {
@@ -124,7 +122,7 @@ public abstract class Round extends RBuiltinNode {
             result[i] = check.check(value) ? RRuntime.DOUBLE_NA : round(value, digits);
         }
         RDoubleVector ret = RDataFactory.createDoubleVector(result, check.neverSeenNA());
-        ret.copyAttributesFrom(attrProfiles, x);
+        ret.copyAttributesFrom(x);
         return ret;
     }
 
@@ -137,7 +135,7 @@ public abstract class Round extends RBuiltinNode {
             result[i] = check.check(value) ? RRuntime.DOUBLE_NA : roundDigits(value, digits);
         }
         RDoubleVector ret = RDataFactory.createDoubleVector(result, check.neverSeenNA());
-        ret.copyAttributesFrom(attrProfiles, x);
+        ret.copyAttributesFrom(x);
         return ret;
     }
 
@@ -165,7 +163,7 @@ public abstract class Round extends RBuiltinNode {
             check.check(r);
         }
         RComplexVector ret = RDataFactory.createComplexVector(result, check.neverSeenNA());
-        ret.copyAttributesFrom(attrProfiles, x);
+        ret.copyAttributesFrom(x);
         return ret;
     }
 
@@ -181,7 +179,7 @@ public abstract class Round extends RBuiltinNode {
             check.check(r);
         }
         RComplexVector ret = RDataFactory.createComplexVector(result, check.neverSeenNA());
-        ret.copyAttributesFrom(attrProfiles, x);
+        ret.copyAttributesFrom(x);
         return ret;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
index 6a4d2b0e3a599bbb0089d72d2dd6ae6b81277597..d9396a717a28d8f3084ab5a169a2ab44bc4fa0ac 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Scan.java
@@ -31,18 +31,18 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.conn.RConnection;
 import com.oracle.truffle.r.runtime.conn.StdConnections;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDouble;
@@ -67,7 +67,7 @@ public abstract class Scan extends RBuiltinNode {
 
     private final NACheck naCheck = NACheck.create();
     private final BranchProfile errorProfile = BranchProfile.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
     @Child private CastToVectorNode castVector;
 
@@ -338,7 +338,7 @@ public abstract class Scan extends RBuiltinNode {
                 list.updateDataAt(i, vec.createEmptySameType(blockSize, RDataFactory.COMPLETE_VECTOR), null);
             }
         }
-        list.setNames(what.getNames(attrProfiles));
+        list.setNames(getNames.getNames(what));
 
         naCheck.enable(true);
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
index e318c352f9a7e3ded51e0bd78d06cfb50c9d1501..17a035de4a7ff78c82f6ecf809bc11439af6a064 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ShortRowNames.java
@@ -31,12 +31,12 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.IntValueProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
@@ -46,10 +46,11 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
 public abstract class ShortRowNames extends RBuiltinNode {
 
     private final BranchProfile naValueMet = BranchProfile.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile errorProfile = BranchProfile.create();
     private final ValueProfile operandTypeProfile = ValueProfile.createClassProfile();
 
+    @Child private GetRowNamesAttributeNode getRowNamesNode = GetRowNamesAttributeNode.create();
+
     @Override
     protected void createCasts(CastBuilder casts) {
         casts.arg("type").asIntegerVector().findFirst().mustBe(gte0().and(lte(2)));
@@ -62,9 +63,9 @@ public abstract class ShortRowNames extends RBuiltinNode {
         Object operand = operandTypeProfile.profile(originalOperand);
         Object rowNames;
         if (operand instanceof RAbstractContainer) {
-            rowNames = ((RAbstractContainer) operand).getRowNames(attrProfiles);
+            rowNames = getRowNamesNode.getRowNames((RAbstractContainer) operand);
         } else if (operand instanceof REnvironment) {
-            rowNames = ((REnvironment) operand).getAttr(attrProfiles, RRuntime.ROWNAMES_ATTR_KEY);
+            rowNames = getRowNamesNode.execute(operand);
         } else {
             // for any other type GnuR returns 0
             return 0;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
index 0313cad4b98aec16177b01b33fe7021ffa3fa6ee..31023607abacfbcd6dffea008880f47efebf75ec 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/ToLowerOrUpper.java
@@ -33,12 +33,13 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -52,9 +53,10 @@ public abstract class ToLowerOrUpper {
         private final VectorLengthProfile lengthProfile = VectorLengthProfile.create();
         private final LoopConditionProfile loopProfile = LoopConditionProfile.createCountingProfile();
         private final NACheck na = NACheck.create();
-        private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
         @Child private CopyOfRegAttributesNode copyAttributes = CopyOfRegAttributesNodeGen.create();
+        @Child private GetDimAttributeNode getDimNode = GetDimAttributeNode.create();
 
         private StringMapNode() {
             // nothing to do
@@ -82,7 +84,7 @@ public abstract class ToLowerOrUpper {
                 String value = vector.getDataAt(i);
                 stringVector[i] = elementFunction(value, i, function);
             }
-            RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), vector.getDimensions(), vector.getNames(attrProfiles));
+            RStringVector result = RDataFactory.createStringVector(stringVector, vector.isComplete(), getDimNode.getDimensions(vector), getNames.getNames(vector));
             copyAttributes.execute(vector, result);
             return result;
         }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
index ec945bb76c07a474ac8f40471b3ab7473c12b4d1..db8fcf308280a90a6933d6a4afd755b6a69584b6 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Transpose.java
@@ -25,14 +25,14 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.CopyOfRegAttributesNodeGen;
-import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -50,7 +50,6 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 @RBuiltin(name = "t.default", kind = INTERNAL, parameterNames = {"x"}, behavior = PURE)
 public abstract class Transpose extends RBuiltinNode {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final BranchProfile hasDimNamesProfile = BranchProfile.create();
     private final ConditionProfile isMatrixProfile = ConditionProfile.createBinaryProfile();
 
@@ -61,6 +60,7 @@ public abstract class Transpose extends RBuiltinNode {
     @Child private InitAttributesNode initAttributes = InitAttributesNode.create();
     @Child private SetFixedAttributeNode putDimensions = SetFixedAttributeNode.createDim();
     @Child private SetFixedAttributeNode putDimNames = SetFixedAttributeNode.createDimNames();
+    @Child private GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
 
     public abstract Object execute(RAbstractVector o);
 
@@ -96,15 +96,13 @@ public abstract class Transpose extends RBuiltinNode {
         copyRegAttributes.execute(vector, r);
         // set new dimensions
         int[] newDim = new int[]{secondDim, firstDim};
-        r.setInternalDimensions(newDim);
         putDimensions.execute(initAttributes.execute(r), RDataFactory.createIntVector(newDim, RDataFactory.COMPLETE_VECTOR));
         // set new dim names
-        RList dimNames = vector.getDimNames(attrProfiles);
+        RList dimNames = getDimNamesNode.getDimNames(vector);
         if (dimNames != null) {
             hasDimNamesProfile.enter();
             assert dimNames.getLength() == 2;
             RList newDimNames = RDataFactory.createList(new Object[]{dimNames.getDataAt(1), dimNames.getDataAt(0)});
-            r.setInternalDimNames(newDimNames);
             putDimNames.execute(r.getAttributes(), newDimNames);
         }
         return r;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
index 03cf59487b7a937e378b58662b29d18458f38740..8558fef08d71acb5d113e9d833217db0c34a7f97 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Unlist.java
@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.UnlistNodeGen.RecursiveLengthNodeGen;
@@ -31,7 +32,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -57,8 +57,7 @@ public abstract class Unlist extends RBuiltinNode {
     @Child private PrecedenceNode precedenceNode = PrecedenceNodeGen.create();
     @Child private Length lengthNode;
     @Child private RecursiveLength recursiveLengthNode;
-
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNames = GetNamesAttributeNode.create();
 
     @TypeSystemReference(RTypes.class)
     protected abstract static class RecursiveLength extends Node {
@@ -189,7 +188,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.RAW_PRECEDENCE: {
                 byte[] result = new byte[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -204,7 +204,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.LOGICAL_PRECEDENCE: {
                 byte[] result = new byte[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -220,7 +221,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.INT_PRECEDENCE: {
                 int[] result = new int[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -236,7 +238,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.DOUBLE_PRECEDENCE: {
                 double[] result = new double[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -252,7 +255,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.COMPLEX_PRECEDENCE: {
                 double[] result = new double[totalSize << 1];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -268,7 +272,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.STRING_PRECEDENCE: {
                 String[] result = new String[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -285,7 +290,8 @@ public abstract class Unlist extends RBuiltinNode {
             case PrecedenceNode.EXPRESSION_PRECEDENCE: {
                 Object[] result = new Object[totalSize];
                 if (!recursive) {
-                    RStringVector listNames = useNames && list.getNames(attrProfiles) != null ? list.getNames(attrProfiles) : null;
+                    RStringVector ln = getNames.getNames(list);
+                    RStringVector listNames = useNames && ln != null ? ln : null;
                     int position = 0;
                     for (int i = 0; i < list.getLength(); i++) {
                         if (list.getDataAt(i) != RNull.instance) {
@@ -337,7 +343,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -373,7 +380,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -409,7 +417,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -445,7 +454,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -481,7 +491,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -522,7 +533,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
@@ -558,7 +570,8 @@ public abstract class Unlist extends RBuiltinNode {
 
         if (o instanceof RAbstractVector) {
             RAbstractVector v = (RAbstractVector) o;
-            RStringVector listNames = useNames && v.getNames(attrProfiles) != null ? v.getNames(attrProfiles) : null;
+            RStringVector ln = getNames.getNames(v);
+            RStringVector listNames = useNames && ln != null ? ln : null;
             for (int i = 0; i < v.getLength(); i++) {
                 String name = itemName(listNames, i);
                 Object cur = v.getDataAtAsObject(i);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index 11bc95e938d5a7b6c2e3a9127e07b22fb9b2d582..34c1d680f8290eead7dfaabaed28894cabb35bd4 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -34,6 +34,10 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
@@ -66,6 +70,10 @@ public abstract class UpdateAttr extends RBuiltinNode {
     @Child private CastIntegerNode castInteger;
     @Child private CastToVectorNode castVector;
     @Child private CastListNode castList;
+    @Child private SetClassAttributeNode setClassAttrNode;
+    @Child private SetRowNamesAttributeNode setRowNamesAttrNode;
+    @Child private SetAttributeNode setGenAttrNode;
+    @Child private SetDimAttributeNode setDimNode;
 
     @CompilationFinal private String cachedName = "";
     @CompilationFinal private String cachedInternedName = "";
@@ -138,15 +146,28 @@ public abstract class UpdateAttr extends RBuiltinNode {
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
         // the name is interned, so identity comparison is sufficient
         if (internedName == RRuntime.DIM_ATTR_KEY) {
-            result.setDimensions(null);
+            if (setDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setDimNode = insert(SetDimAttributeNode.create());
+            }
+            setDimNode.setDimensions(result, null);
         } else if (internedName == RRuntime.NAMES_ATTR_KEY) {
             return updateNames(result, value);
         } else if (internedName == RRuntime.DIMNAMES_ATTR_KEY) {
             return updateDimNames(result, value);
         } else if (internedName == RRuntime.CLASS_ATTR_KEY) {
-            return (RAbstractContainer) result.setClassAttr(null);
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+            setClassAttrNode.reset(result);
+            return result;
         } else if (internedName == RRuntime.ROWNAMES_ATTR_KEY) {
-            result.setRowNames(null);
+            if (setRowNamesAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setRowNamesAttrNode = insert(SetRowNamesAttributeNode.create());
+            }
+            setRowNamesAttrNode.setRowNames(result, null);
         } else if (result.getAttributes() != null) {
             result.removeAttr(attrProfiles, internedName);
         }
@@ -175,18 +196,35 @@ public abstract class UpdateAttr extends RBuiltinNode {
                 errorProfile.enter();
                 throw RError.error(this, RError.Message.LENGTH_ZERO_DIM_INVALID);
             }
-            result.setDimensions(dimsVector.materialize().getDataCopy());
+            if (setDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setDimNode = insert(SetDimAttributeNode.create());
+            }
+            setDimNode.setDimensions(result, dimsVector.materialize().getDataCopy());
         } else if (internedName == RRuntime.NAMES_ATTR_KEY) {
             return updateNames(result, value);
         } else if (internedName == RRuntime.DIMNAMES_ATTR_KEY) {
             return updateDimNames(result, value);
         } else if (internedName == RRuntime.CLASS_ATTR_KEY) {
-            return (RAbstractContainer) result.setClassAttr(convertClassAttrFromObject(value));
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+            setClassAttrNode.execute(result, convertClassAttrFromObject(value));
+            return result;
         } else if (internedName == RRuntime.ROWNAMES_ATTR_KEY) {
-            result.setRowNames(castVector(value));
+            if (setRowNamesAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setRowNamesAttrNode = insert(SetRowNamesAttributeNode.create());
+            }
+            setRowNamesAttrNode.setRowNames(result, castVector(value));
         } else {
             // generic attribute
-            result.setAttr(internedName, value);
+            if (setGenAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setGenAttrNode = insert(SetAttributeNode.create());
+            }
+            setGenAttrNode.execute(result, internedName, value);
         }
 
         return result;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
index 31ce0f85a414a66a52bc6d3757e9398960508d74..4e0c99102cbac929b1bff3705111ca5f2e9f4f43 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttributes.java
@@ -31,6 +31,10 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
@@ -55,11 +59,15 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class UpdateAttributes extends RBuiltinNode {
     private final ConditionProfile numAttributesProfile = ConditionProfile.createBinaryProfile();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Child private UpdateNames updateNames;
     @Child private UpdateDimNames updateDimNames;
     @Child private CastIntegerNode castInteger;
     @Child private CastToVectorNode castVector;
+    @Child private SetAttributeNode setAttrNode;
+    @Child private SetDimAttributeNode setDimNode;
+    @Child private SetRowNamesAttributeNode setRowNamesNode;
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -113,7 +121,7 @@ public abstract class UpdateAttributes extends RBuiltinNode {
 
     @Specialization
     protected RAbstractContainer updateAttributes(RAbstractContainer container, RList list) {
-        Object listNamesObject = list.getNames(attrProfiles);
+        Object listNamesObject = getNamesNode.getNames(list);
         if (listNamesObject == null || listNamesObject == RNull.instance) {
             throw RError.error(this, RError.Message.ATTRIBUTES_NAMED);
         }
@@ -154,28 +162,34 @@ public abstract class UpdateAttributes extends RBuiltinNode {
     }
 
     private void setDimAttribute(RAbstractContainer result, RList sourceList) {
-        RStringVector listNames = sourceList.getNames(attrProfiles);
+        RStringVector listNames = getNamesNode.getNames(sourceList);
         int length = sourceList.getLength();
         assert length > 0 : "Length should be > 0 for ExplodeLoop";
         for (int i = 0; i < sourceList.getLength(); i++) {
             Object value = sourceList.getDataAt(i);
             String attrName = listNames.getDataAt(i);
             if (attrName.equals(RRuntime.DIM_ATTR_KEY)) {
+
+                if (setDimNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setDimNode = insert(SetDimAttributeNode.create());
+                }
+
                 if (value == RNull.instance) {
-                    result.setDimensions(null);
+                    setDimNode.setDimensions(result, null);
                 } else {
                     RAbstractIntVector dimsVector = castInteger(castVector(value));
                     if (dimsVector.getLength() == 0) {
                         throw RError.error(this, RError.Message.LENGTH_ZERO_DIM_INVALID);
                     }
-                    result.setDimensions(dimsVector.materialize().getDataCopy());
+                    setDimNode.setDimensions(result, dimsVector.materialize().getDataCopy());
                 }
             }
         }
     }
 
     private RAbstractContainer setRemainingAttributes(RAbstractContainer result, RList sourceList) {
-        RStringVector listNames = sourceList.getNames(attrProfiles);
+        RStringVector listNames = getNamesNode.getNames(sourceList);
         int length = sourceList.getLength();
         assert length > 0 : "Length should be > 0 for ExplodeLoop";
         RAbstractContainer res = result;
@@ -190,17 +204,26 @@ public abstract class UpdateAttributes extends RBuiltinNode {
                 res = updateDimNames(res, value);
             } else if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
                 if (value == RNull.instance) {
-                    res = (RAbstractContainer) result.setClassAttr(null);
+                    res.setClassAttr(null);
                 } else {
-                    res = (RAbstractContainer) result.setClassAttr(UpdateAttr.convertClassAttrFromObject(value));
+                    res.setClassAttr(UpdateAttr.convertClassAttrFromObject(value));
                 }
+                res = result;
             } else if (attrName.equals(RRuntime.ROWNAMES_ATTR_KEY)) {
-                res.setRowNames(castVector(value));
+                if (setRowNamesNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setRowNamesNode = insert(SetRowNamesAttributeNode.create());
+                }
+                setRowNamesNode.setRowNames(res, castVector(value));
             } else {
                 if (value == RNull.instance) {
                     res.removeAttr(attrProfiles, attrName);
                 } else {
-                    res.setAttr(attrName.intern(), value);
+                    if (setAttrNode == null) {
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                        setAttrNode = insert(SetAttributeNode.create());
+                    }
+                    setAttrNode.execute(res, attrName.intern(), value);
                 }
             }
         }
@@ -251,6 +274,7 @@ public abstract class UpdateAttributes extends RBuiltinNode {
                 if (attrValue == null) {
                     throw RError.error(this, RError.Message.SET_INVALID_CLASS_ATTR);
                 }
+
                 attrObj.setClassAttr(UpdateAttr.convertClassAttrFromObject(attrValue));
             } else {
                 attrObj.setAttr(attrName.intern(), operand.getDataAt(i));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
index 62799507ade961086ec2c43d64c72aa344899b69..094f7fb3b8b052cc251da25e495c326f181a1cf3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateClass.java
@@ -18,6 +18,8 @@ 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.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.binary.CastTypeNode;
 import com.oracle.truffle.r.nodes.binary.CastTypeNodeGen;
@@ -48,6 +50,7 @@ public abstract class UpdateClass extends RBuiltinNode {
 
     @Child private CastTypeNode castTypeNode;
     @Child private TypeofNode typeof;
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
 
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
@@ -61,7 +64,8 @@ public abstract class UpdateClass extends RBuiltinNode {
     @TruffleBoundary
     protected Object setClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         RAbstractContainer result = reuseNonShared(arg);
-        return result.setClassAttr(null);
+        setClassAttrNode.reset(result);
+        return result;
     }
 
     @Specialization(limit = "CACHE_LIMIT", guards = "cachedClassName == className")
@@ -94,6 +98,7 @@ public abstract class UpdateClass extends RBuiltinNode {
                 return setClass((RAbstractVector) result, RNull.instance);
             }
         }
+
         RAbstractContainer result = reuseNonShared(arg);
         if (result instanceof RAbstractVector) {
             RAbstractVector resultVector = (RAbstractVector) result;
@@ -114,73 +119,75 @@ public abstract class UpdateClass extends RBuiltinNode {
             }
         }
 
-        return result.setClassAttr(RDataFactory.createStringVector(className));
+        setClassAttrNode.execute(result, RDataFactory.createStringVector(className));
+        return result;
     }
 
     @Specialization
     @TruffleBoundary
     protected Object setClass(RAbstractContainer arg, RStringVector className) {
         RAbstractContainer result = reuseNonShared(arg);
-        return result.setClassAttr(className);
+        setClassAttrNode.execute(result, className);
+        return result;
     }
 
     @Specialization
     protected Object setClass(RFunction arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RFunction arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(REnvironment arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(REnvironment arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RSymbol arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RSymbol arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RExternalPtr arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RExternalPtr arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
     @Specialization
     protected Object setClass(RS4Object arg, RAbstractStringVector className) {
-        arg.setClassAttr(className.materialize());
+        setClassAttrNode.execute(arg, className.materialize());
         return arg;
     }
 
     @Specialization
     protected Object setClass(RS4Object arg, @SuppressWarnings("unused") RNull className) {
-        arg.setClassAttr(null);
+        setClassAttrNode.reset(arg);
         return arg;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
index 0b7c49004d52e746b872d503f89b2e7fa32376aa..d30e638139ad629645041ed73dab870e8b043753 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDim.java
@@ -31,14 +31,12 @@ 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.ConditionProfile;
-import com.oracle.truffle.r.nodes.attributes.InitAttributesNode;
+import com.oracle.truffle.r.nodes.attributes.RemoveFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.opt.ReuseNonSharedNode;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -67,14 +65,13 @@ public abstract class UpdateDim extends RBuiltinNode {
     @Specialization
     protected RAbstractVector updateDim(RAbstractVector vector, RAbstractIntVector dimensions,
                     @Cached("createBinaryProfile()") ConditionProfile initAttrProfile,
-                    @Cached("createDim()") SetFixedAttributeNode putDimensions) {
+                    @Cached("createDim()") SetFixedAttributeNode putDimensions,
+                    @Cached("createNames()") RemoveFixedAttributeNode removeNames) {
         RIntVector dimensionsMaterialized = dimensions.materialize();
         int[] dimsData = dimensionsMaterialized.getDataCopy();
         RVector.verifyDimensions(vector.getLength(), dimsData, this);
         RVector<?> result = ((RAbstractVector) reuse.execute(vector)).materialize();
-        result.setInternalDimensions(dimsData);
-        result.setInternalNames(null);
-        result.setInternalDimNames(null);
+        removeNames.execute(result);
 
         DynamicObject attrs = result.getAttributes();
         if (initAttrProfile.profile(attrs == null)) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
index b5ee7967ce49f2b74af6722eca32380187520043..35adf33049ff8328091ce27e59d1947407474c8e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateDimNames.java
@@ -31,7 +31,7 @@ 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.r.nodes.attributes.RemoveFixedAttributeNode;
-import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
@@ -40,11 +40,8 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
@@ -54,7 +51,6 @@ public abstract class UpdateDimNames extends RBuiltinNode {
     protected static final String DIMNAMES_ATTR_KEY = RRuntime.DIMNAMES_ATTR_KEY;
 
     private final ConditionProfile shareListProfile = ConditionProfile.createBinaryProfile();
-    private final ConditionProfile isRVectorProfile = ConditionProfile.createBinaryProfile();
 
     @Child private CastStringNode castStringNode;
     @Child private CastToVectorNode castVectorNode;
@@ -96,15 +92,7 @@ public abstract class UpdateDimNames extends RBuiltinNode {
     protected RAbstractContainer updateDimnamesNull(RAbstractContainer container, @SuppressWarnings("unused") RNull list, //
                     @Cached("createDimNames()") RemoveFixedAttributeNode remove) {
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
-        if (isRVectorProfile.profile(container instanceof RVector)) {
-            RVector<?> vector = (RVector<?>) result;
-            if (vector.getInternalDimNames() != null) {
-                vector.setInternalDimNames(null);
-                remove.execute(vector.getAttributes());
-            }
-        } else {
-            result.setDimNames(null);
-        }
+        remove.execute(result);
         return result;
     }
 
@@ -116,9 +104,9 @@ public abstract class UpdateDimNames extends RBuiltinNode {
 
     @Specialization(guards = "list.getLength() > 0")
     protected RAbstractContainer updateDimnames(RAbstractContainer container, RList list, //
-                    @Cached("createDimNames()") SetFixedAttributeNode attrSetter) {
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode) {
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
-        setDimNames(result, convertToListOfStrings(list), attrSetter);
+        setDimNamesNode.setDimNames(result, convertToListOfStrings(list));
         return result;
     }
 
@@ -128,58 +116,4 @@ public abstract class UpdateDimNames extends RBuiltinNode {
         throw RError.error(this, RError.Message.DIMNAMES_LIST);
     }
 
-    private void setDimNames(RAbstractContainer container, RList newDimNames, SetFixedAttributeNode attrSetter) {
-        assert newDimNames != null;
-        if (isRVectorProfile.profile(container instanceof RVector)) {
-            RVector<?> vector = (RVector<?>) container;
-            int[] dimensions = vector.getDimensions();
-            if (dimensions == null) {
-                CompilerDirectives.transferToInterpreter();
-                throw RError.error(this, RError.Message.DIMNAMES_NONARRAY);
-            }
-            int newDimNamesLength = newDimNames.getLength();
-            if (newDimNamesLength > dimensions.length) {
-                CompilerDirectives.transferToInterpreter();
-                throw RError.error(this, RError.Message.DIMNAMES_DONT_MATCH_DIMS, newDimNamesLength, dimensions.length);
-            }
-            for (int i = 0; i < newDimNamesLength; i++) {
-                Object dimObject = newDimNames.getDataAt(i);
-                if (dimObject != RNull.instance) {
-                    if (dimObject instanceof String) {
-                        if (dimensions[i] != 1) {
-                            CompilerDirectives.transferToInterpreter();
-                            throw RError.error(this, RError.Message.DIMNAMES_DONT_MATCH_EXTENT, i + 1);
-                        }
-                    } else {
-                        RStringVector dimVector = (RStringVector) dimObject;
-                        if (dimVector == null || dimVector.getLength() == 0) {
-                            newDimNames.updateDataAt(i, RNull.instance, null);
-                        } else if (dimVector.getLength() != dimensions[i]) {
-                            CompilerDirectives.transferToInterpreter();
-                            throw RError.error(this, RError.Message.DIMNAMES_DONT_MATCH_EXTENT, i + 1);
-                        }
-                    }
-                }
-            }
-
-            RList resDimNames = newDimNames;
-            if (newDimNamesLength < dimensions.length) {
-                // resize the array and fill the missing entries with NULL-s
-                resDimNames = (RList) resDimNames.copyResized(dimensions.length, true);
-                resDimNames.setAttributes(newDimNames);
-                for (int i = newDimNamesLength; i < dimensions.length; i++) {
-                    resDimNames.updateDataAt(i, RNull.instance, null);
-                }
-            }
-            if (vector.getAttributes() == null) {
-                vector.initAttributes(RAttributesLayout.createDimNames(resDimNames));
-            } else {
-                attrSetter.execute(vector.getAttributes(), resDimNames);
-            }
-            resDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
-            vector.setInternalDimNames(resDimNames);
-        } else {
-            container.setDimNames(newDimNames);
-        }
-    }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
index b9f41698d5966fe4c557badf8f03da94e0b95225..329730c362cfc6ee005247514fa33506811d928e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateLevels.java
@@ -15,7 +15,9 @@ import static com.oracle.truffle.r.runtime.RDispatch.INTERNAL_GENERIC;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -44,10 +46,15 @@ public abstract class UpdateLevels extends RBuiltinNode {
         return v;
     }
 
+    protected SetFixedAttributeNode createSetLevelsAttrNode() {
+        return SetFixedAttributeNode.create(RRuntime.LEVELS_ATTR_KEY);
+    }
+
     @Specialization(guards = "!isRNull(levels)")
-    protected RAbstractVector updateLevels(RAbstractVector vector, Object levels) {
+    protected RAbstractVector updateLevels(RAbstractVector vector, Object levels,
+                    @Cached("createSetLevelsAttrNode()") SetFixedAttributeNode setLevelsAttrNode) {
         RVector<?> v = (RVector<?>) vector.getNonShared();
-        v.setAttr(RRuntime.LEVELS_ATTR_KEY, levels);
+        setLevelsAttrNode.execute(v, levels);
         return v;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
index f05261a585f42113af69f41576f5a2897eddc6df..02c1ab3e723189351e01281fc7714e35326c9270 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateOldClass.java
@@ -29,6 +29,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
@@ -45,6 +46,7 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 public abstract class UpdateOldClass extends RBuiltinNode {
 
     @Child private CastStringNode castStringNode;
+    @Child private SetClassAttributeNode setClassAttributeNode = SetClassAttributeNode.create();
 
     @Specialization(guards = "!isStringVector(className)")
     protected Object setOldClass(RAbstractContainer arg, RAbstractVector className) {
@@ -73,14 +75,16 @@ public abstract class UpdateOldClass extends RBuiltinNode {
     @TruffleBoundary
     protected Object setOldClass(RAbstractContainer arg, RStringVector className) {
         RAbstractContainer result = (RAbstractContainer) arg.getNonShared();
-        return result.setClassAttr(className);
+        setClassAttributeNode.execute(result, className);
+        return result;
     }
 
     @Specialization
     @TruffleBoundary
     protected Object setOldClass(RAbstractContainer arg, @SuppressWarnings("unused") RNull className) {
         RAbstractContainer result = (RAbstractContainer) arg.getNonShared();
-        return result.setClassAttr(null);
+        setClassAttributeNode.reset(result);
+        return result;
     }
 
     protected boolean isStringVector(RAbstractVector className) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
index a2b8ddcf234c55e836d8af7397a79d7e2ea40423..804b73b66704a390fa5dee5d937b25a7c650b76d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateStorageMode.java
@@ -20,6 +20,8 @@ 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.r.nodes.attributes.ArrayAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNode;
 import com.oracle.truffle.r.nodes.attributes.TypeFromModeNodeGen;
 import com.oracle.truffle.r.nodes.binary.CastTypeNode;
@@ -35,9 +37,7 @@ import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
 @RBuiltin(name = "storage.mode<-", kind = PRIMITIVE, parameterNames = {"x", "value"}, behavior = PURE)
@@ -47,11 +47,14 @@ public abstract class UpdateStorageMode extends RBuiltinNode {
     @Child private TypeofNode typeof;
     @Child private CastTypeNode castTypeNode;
     @Child private IsFactorNode isFactor;
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     private final BranchProfile errorProfile = BranchProfile.create();
 
     @Specialization
-    protected Object update(Object x, String value, @Cached("create()") ArrayAttributeNode attrAttrAccess) {
+    protected Object update(Object x, String value,
+                    @Cached("create()") ArrayAttributeNode attrAttrAccess,
+                    @Cached("create()") SetAttributeNode setAttrNode) {
         RType mode = typeFromMode.execute(value);
         if (mode == RType.DefunctReal || mode == RType.DefunctSingle) {
             errorProfile.enter();
@@ -80,13 +83,19 @@ public abstract class UpdateStorageMode extends RBuiltinNode {
                             String attrName = attr.getName();
                             Object v = attr.getValue();
                             if (attrName.equals(RRuntime.CLASS_ATTR_KEY)) {
+
+                                if (setClassAttrNode == null) {
+                                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                                    setClassAttrNode = insert(SetClassAttributeNode.create());
+                                }
+
                                 if (v == RNull.instance) {
-                                    rresult = (RAbstractContainer) rresult.setClassAttr(null);
+                                    setClassAttrNode.reset(rresult);
                                 } else {
-                                    rresult = (RAbstractContainer) rresult.setClassAttr((RStringVector) v);
+                                    setClassAttrNode.execute(rresult, v);
                                 }
                             } else {
-                                rresult.setAttr(Utils.intern(attrName), v);
+                                setAttrNode.execute(rresult, Utils.intern(attrName), v);
                             }
                         }
                         return rresult;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
index 2fdba2452bbd296c15841f87e7aead60fb576a88..83f99a9513a2e09bb32b33e7d754e008d0e4af4a 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/VApply.java
@@ -32,6 +32,9 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.Lapply.LapplyInternalNode;
@@ -50,7 +53,6 @@ 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.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -80,7 +82,6 @@ public abstract class VApply extends RBuiltinNode {
 
     private final ConditionProfile useNamesProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile dimsProfile = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     private final NACheck naCheck = NACheck.create();
 
     @Child private LapplyInternalNode doApply = LapplyInternalNodeGen.create();
@@ -90,6 +91,9 @@ public abstract class VApply extends RBuiltinNode {
     @Child private CastIntegerNode castInteger;
     @Child private CastLogicalNode castLogical;
     @Child private CastStringNode castString;
+    @Child private SetDimAttributeNode setDimNode;
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
+    @Child private SetNamesAttributeNode setNamesNode = SetNamesAttributeNode.create();
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -186,12 +190,16 @@ public abstract class VApply extends RBuiltinNode {
         }
 
         if (dimsProfile.profile(funValueVecLen > 1)) {
-            result.setDimensions(new int[]{funValueVecLen, applyResult.length});
+            if (setDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setDimNode = insert(SetDimAttributeNode.create());
+            }
+            setDimNode.setDimensions(result, new int[]{funValueVecLen, applyResult.length});
         }
 
         // TODO: handle names in case of matrices
         if (useNamesProfile.profile(RRuntime.fromLogical(useNames))) {
-            RStringVector names = vecMat.getNames(attrProfiles);
+            RStringVector names = getNamesNode.getNames(vecMat);
             RStringVector newNames = null;
             if (names != null) {
                 newNames = names;
@@ -199,7 +207,7 @@ public abstract class VApply extends RBuiltinNode {
                 newNames = (RStringVector) vecMat.copy();
             }
             if (newNames != null) {
-                result.setNames(newNames);
+                setNamesNode.setNames(result, newNames);
             }
         }
         return result;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
index ec76dd55c89e32226ef5eafac0317d653103d41b..14614074f986b56a2698b9eccd9b6a13189b4b8d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/WhichFunctions.java
@@ -30,6 +30,7 @@ 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.LoopConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.builtin.base.WhichFunctionsFactory.WhichMaxNodeGen;
@@ -37,7 +38,6 @@ import com.oracle.truffle.r.nodes.builtin.base.WhichFunctionsFactory.WhichMinNod
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RIntVector;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -64,7 +64,7 @@ public class WhichFunctions {
                         @Cached("create()") VectorLengthProfile lengthProfile,
                         @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
                         @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
-                        @Cached("create()") RAttributeProfiles attrProfiles,
+                        @Cached("create()") GetNamesAttributeNode getNamesNode,
                         @Cached("create()") NACheck naCheck) {
             int length = lengthProfile.profile(x.getLength());
             loopProfile.profileCounted(length);
@@ -83,7 +83,7 @@ public class WhichFunctions {
                     result[pos++] = i + 1;
                 }
             }
-            RStringVector names = x.getNames(attrProfiles);
+            RStringVector names = getNamesNode.getNames(x);
             if (hasNamesProfile.profile(names != null)) {
                 // collect result names
                 String[] resultNames = new String[resultLength];
@@ -122,7 +122,7 @@ public class WhichFunctions {
                         @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
                         @Cached("createBinaryProfile()") ConditionProfile isNaNProfile,
                         @Cached("createBinaryProfile()") ConditionProfile hasNamesProfile,
-                        @Cached("create()") RAttributeProfiles attrProfiles) {
+                        @Cached("create()") GetNamesAttributeNode getNamesNode) {
             int length = lengthProfile.profile(x.getLength());
             loopProfile.profileCounted(length);
             double extreme = Double.NaN;
@@ -138,7 +138,7 @@ public class WhichFunctions {
             if (isNaNProfile.profile(extremeIndex == -1)) {
                 return RDataFactory.createEmptyIntVector();
             }
-            RStringVector names = x.getNames(attrProfiles);
+            RStringVector names = getNamesNode.getNames(x);
             if (hasNamesProfile.profile(names != null)) {
                 // collect result names
                 RStringVector resultNames = RDataFactory.createStringVectorFromScalar(names.getDataAt(extremeIndex));
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
index a8ac59b7406f81e36680f9a5cf2596b7d4b3c6d8..3ca0f0c97d0173a30490c3c5cdd8b0bf225426ec 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrcf.java
@@ -49,7 +49,7 @@ public final class Dqrcf extends RExternalBuiltinNode {
             int[] info = infoVec.materialize().getDataTemp();
             rApplRFFINode.dqrcf(x, n, k.getDataAt(0), qraux, y, ny, b, info);
             RDoubleVector coef = RDataFactory.createDoubleVector(b, RDataFactory.COMPLETE_VECTOR);
-            coef.copyAttributesFrom(attrProfiles, bVec);
+            coef.copyAttributesFrom(bVec);
             // @formatter:off
             Object[] data = new Object[]{
                         RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR),
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
index 7c9ae20b0b01c46ebfc2fb1f4fe44946c839c083..e63b42dd1195018bf6b83324ff0ca72ef2dcdfb1 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Dqrdc2.java
@@ -11,6 +11,7 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -29,6 +30,8 @@ public final class Dqrdc2 extends RExternalBuiltinNode {
     private static final String E = RRuntime.NAMES_ATTR_EMPTY_VALUE;
     private static final RStringVector DQRDC2_NAMES = RDataFactory.createStringVector(new String[]{"qr", E, E, E, E, "rank", "qraux", "pivot", E}, RDataFactory.COMPLETE_VECTOR);
 
+    @Child private GetDimAttributeNode getDimNode = GetDimAttributeNode.create();
+
     @Override
     public RList call(RArgsValuesAndNames args) {
         Object[] argValues = args.getArguments();
@@ -49,7 +52,7 @@ public final class Dqrdc2 extends RExternalBuiltinNode {
             rApplRFFINode.dqrdc2(x, ldx, n, p, tol, rank, qraux, pivot, workVec.materialize().getDataCopy());
             // @formatter:off
             Object[] data = new Object[]{
-                        RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR, xVec.getDimensions()),
+                        RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR, getDimNode.getDimensions(xVec)),
                         argValues[1], argValues[2], argValues[3], argValues[4],
                         RDataFactory.createIntVector(rank, RDataFactory.COMPLETE_VECTOR),
                         RDataFactory.createDoubleVector(qraux, RDataFactory.COMPLETE_VECTOR),
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
index 31db4963325002d4c9ddb1db5826bcefc7247f2a..b4bf5517d216c83e638f04f7e139142fa7278a16 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/Fft.java
@@ -11,8 +11,10 @@
  */
 package com.oracle.truffle.r.nodes.builtin.base.foreign;
 
+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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
@@ -36,15 +38,16 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
 
     // TODO: handle more argument types (this is sufficient to run the b25 benchmarks)
     @Specialization
-    public Object execute(RAbstractComplexVector zVec, boolean inverse) {
+    public Object execute(RAbstractComplexVector zVec, boolean inverse, @Cached("create()") GetDimAttributeNode getDimNode) {
         double[] z = zVec.materialize().getDataTemp();
         int inv = inverse ? 2 : -2;
+        int[] d = getDimNode.getDimensions(zVec);
         @SuppressWarnings("unused")
         int retCode = 7;
         if (zVecLgt1.profile(zVec.getLength() > 1)) {
             int[] maxf = new int[1];
             int[] maxp = new int[1];
-            if (noDims.profile(zVec.getDimensions() == null)) {
+            if (noDims.profile(d == null)) {
                 int n = zVec.getLength();
                 fftNode.executeFactor(n, maxf, maxp);
                 if (maxf[0] == 0) {
@@ -57,7 +60,6 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
             } else {
                 int maxmaxf = 1;
                 int maxmaxp = 1;
-                int[] d = zVec.getDimensions();
                 int ndims = d.length;
                 /* do whole loop just for error checking and maxmax[fp] .. */
                 for (int i = 0; i < ndims; i++) {
@@ -91,6 +93,6 @@ public abstract class Fft extends RExternalBuiltinNode.Arg2 {
                 }
             }
         }
-        return RDataFactory.createComplexVector(z, zVec.isComplete(), zVec.getDimensions());
+        return RDataFactory.createComplexVector(z, zVec.isComplete(), d);
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
index 36866074464c2b3f440152f76350d05a393b6dbc..9f848c51562ad640a6ac5c7331ebdd9be1c13c2b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java
@@ -39,6 +39,7 @@ import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractListElement;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
@@ -69,8 +70,8 @@ abstract class AccessFieldSpecial extends SpecialsUtils.ListFieldSpecialBase {
     }
 
     @Specialization(contains = "doList", guards = {"isSimpleList(list)", "list.getNames() != null"})
-    public Object doListDynamic(RList list, String field) {
-        return doList(list, field, getIndex(list.getNames(), field));
+    public Object doListDynamic(RList list, String field, @Cached("create()") GetNamesAttributeNode getNamesNode) {
+        return doList(list, field, getIndex(getNamesNode.getNames(list), field));
     }
 
     @Fallback
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java
index a72d60cf059516649d731875c9f2352777135446..793d882e1a8121d8e29bcbdbcd993e9b78bbe6d3 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/SpecialsUtils.java
@@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -84,12 +85,13 @@ class SpecialsUtils {
         @CompilationFinal private String cachedField;
         @CompilationFinal private RStringVector cachedNames;
         @Child private ClassHierarchyNode hierarchyNode = ClassHierarchyNode.create();
+        @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         protected final void updateCache(RList list, String field) {
             if (cachedField == null) {
                 CompilerDirectives.transferToInterpreterAndInvalidate();
                 cachedField = field;
-                cachedNames = list.getNames();
+                cachedNames = getNamesNode.getNames(list);
             }
         }
 
@@ -98,7 +100,7 @@ class SpecialsUtils {
         }
 
         protected final boolean isCached(RList list, String field) {
-            return cachedField == null || (cachedField == field && list.getNames() == cachedNames);
+            return cachedField == null || (cachedField == field && getNamesNode.getNames(list) == cachedNames);
         }
 
         protected static int getIndex(RStringVector names, String field) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
index 4b3166024df80800419b87a3ec2843f9f62f46b7..9d693de22845ef0dba043d41824103eeda08015e 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java
@@ -34,12 +34,12 @@ import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode;
 import com.oracle.truffle.r.nodes.access.vector.ExtractListElement;
 import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RLogical;
@@ -55,12 +55,12 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 @TypeSystemReference(EmptyTypeSystemFlatLayout.class)
 abstract class SubsetSpecial extends SubscriptSpecialBase {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     @Override
     protected boolean simpleVector(RAbstractVector vector) {
         vector = vectorClassProfile.profile(vector);
-        return super.simpleVector(vector) && vector.getNames(attrProfiles) == null;
+        return super.simpleVector(vector) && getNamesNode.getNames(vector) == null;
     }
 
     @Override
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
index 771bc4bd590e1e2d170aacb2159ceca8c77d16c5..1918c7c7447a23370355b722e6bcf4ba139faeb0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Tilde.java
@@ -25,8 +25,11 @@ package com.oracle.truffle.r.nodes.builtin.base.infix;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.READS_FRAME;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -49,18 +52,25 @@ public abstract class Tilde extends RBuiltinNode {
 
     private static final RStringVector FORMULA_CLASS = RDataFactory.createStringVectorFromScalar(RRuntime.FORMULA_CLASS);
 
+    @Child private SetClassAttributeNode setClassAttrNode = SetClassAttributeNode.create();
+
     @Override
     public Object[] getDefaultParameterValues() {
         return new Object[]{RMissing.instance, RMissing.instance};
     }
 
+    protected SetFixedAttributeNode createSetEnvAttrNode() {
+        return SetFixedAttributeNode.create(RRuntime.DOT_ENVIRONMENT);
+    }
+
     @Specialization
-    protected RLanguage tilde(VirtualFrame frame, @SuppressWarnings("unused") Object response, @SuppressWarnings("unused") Object model) {
+    protected RLanguage tilde(VirtualFrame frame, @SuppressWarnings("unused") Object response, @SuppressWarnings("unused") Object model,
+                    @Cached("createSetEnvAttrNode()") SetFixedAttributeNode setEnvAttrNode) {
         RCallNode call = (RCallNode) ((RBaseNode) getParent()).asRSyntaxNode();
         RLanguage lang = RDataFactory.createLanguage(call);
-        lang.setClassAttr(FORMULA_CLASS);
+        setClassAttrNode.execute(lang, FORMULA_CLASS);
         REnvironment env = REnvironment.frameToEnvironment(frame.materialize());
-        lang.setAttr(RRuntime.DOT_ENVIRONMENT, env);
+        setEnvAttrNode.execute(lang, env);
         return lang;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
index da44b6344da226739efbb89c77cbb9067773ccbe..ebbd01a056c1e63bb5e0436186690bb062b07a2c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java
@@ -83,7 +83,7 @@ abstract class UpdateFieldSpecial extends SpecialsUtils.ListFieldSpecialBase {
 
     @Specialization(contains = "doList", guards = {"isSimpleList(list)", "!list.isShared()", "list.getNames() != null", "isNotRNullRList(value)"})
     public RList doListDynamic(RList list, String field, Object value) {
-        return doList(list, field, value, getIndex(list.getNames(), field));
+        return doList(list, field, value, getIndex(getNamesNode.getNames(list), field));
     }
 
     @SuppressWarnings("unused")
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExpressionPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExpressionPrinter.java
index d8fcdd5d2926f7fbae17bbc8913cc2097940d3e5..6bbd827d0354c7ff5428f5c4a1bc7a1fbb82a087 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExpressionPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ExpressionPrinter.java
@@ -26,8 +26,6 @@ import java.io.IOException;
 import java.io.PrintWriter;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 
@@ -39,8 +37,6 @@ final class ExpressionPrinter extends AbstractValuePrinter<RExpression> {
         // singleton
     }
 
-    private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
-
     @Override
     @TruffleBoundary
     protected void printValue(RExpression expr, PrintContext printCtx) throws IOException {
@@ -49,7 +45,7 @@ final class ExpressionPrinter extends AbstractValuePrinter<RExpression> {
         valPrintCtx.parameters().setSuppressIndexLabels(true);
 
         out.print("expression(");
-        RStringVector names = (RStringVector) expr.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY);
+        RStringVector names = expr.getNames();
         for (int i = 0; i < expr.getLength(); i++) {
             if (i != 0) {
                 out.print(", ");
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java
index ffc0c1db1b5e9877d742687455e746f049aa7cb3..62a994e753e86d30a52158f32994a1dbba4891ae 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ListPrinter.java
@@ -44,20 +44,19 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> {
 
     static final ListPrinter INSTANCE = new ListPrinter();
+    private static final RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
 
     private ListPrinter() {
         // singleton
     }
 
-    private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
-
     private static int TAGBUFLEN = 256;
 
     @Override
     @TruffleBoundary
     protected void printValue(RAbstractListVector s, PrintContext printCtx) throws IOException {
         RAbstractIntVector dims = Utils.<RAbstractIntVector> castTo(
-                        s.getAttr(dummyAttrProfiles, RRuntime.DIM_ATTR_KEY));
+                        s.getAttr(RRuntime.DIM_ATTR_KEY));
 
         if (dims != null && dims.getLength() > 1) {
             printDimList(s, printCtx);
@@ -151,7 +150,7 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> {
         }
 
         RStringVector tt = RDataFactory.createStringVector(t, true, s.getDimensions());
-        Object dimNames = s.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY);
+        Object dimNames = s.getAttr(RRuntime.DIMNAMES_ATTR_KEY);
         tt.setAttr(RRuntime.DIMNAMES_ATTR_KEY, dimNames);
 
         PrintContext cc = printCtx.cloneContext();
@@ -171,7 +170,7 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> {
         int ns = s.getLength();
 
         RAbstractStringVector names;
-        names = Utils.castTo(RRuntime.asAbstractVector(s.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY)));
+        names = Utils.castTo(RRuntime.asAbstractVector(s.getNames(dummyAttrProfiles)));
 
         if (ns > 0) {
             int npr = (ns <= pp.getMax() + 1) ? ns : pp.getMax();
@@ -216,7 +215,7 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> {
 
                 out.println(tagbuf);
                 Object si = s.getDataAtAsObject(i);
-                if (si instanceof RAttributable && ((RAttributable) si).isObject(dummyAttrProfiles)) {
+                if (si instanceof RAttributable && ((RAttributable) si).isObject()) {
                     RContext.getEngine().printResult(si);
                 } else {
                     ValuePrinters.INSTANCE.print(si, printCtx);
@@ -236,7 +235,7 @@ final class ListPrinter extends AbstractValuePrinter<RAbstractListVector> {
             /* Formal classes are represented as empty lists */
             String className = null;
             if (printCtx.printerNode().isObject(s) && printCtx.printerNode().isMethodDispatchOn()) {
-                RAbstractStringVector klass = Utils.castTo(RRuntime.asAbstractVector(s.getAttr(dummyAttrProfiles, RRuntime.CLASS_ATTR_KEY)));
+                RAbstractStringVector klass = Utils.castTo(RRuntime.asAbstractVector(s.getAttr(RRuntime.CLASS_ATTR_KEY)));
                 if (klass != null && klass.getLength() == 1) {
                     String ss = klass.getDataAt(0);
                     String str = snprintf(200, ".__C__%s", ss);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java
index 9dabad114d57463555f70c483b5dbc381a9abc9b..5927ddf98ec477d3b0d8c3dc191785040e0831f2 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java
@@ -17,7 +17,6 @@ import java.io.IOException;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RLanguage;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -42,13 +41,11 @@ final class PairListPrinter extends AbstractValuePrinter<RPairList> {
         // singleton
     }
 
-    private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
-
     @Override
     @TruffleBoundary
     protected void printValue(RPairList s, PrintContext printCtx) throws IOException {
         final RAbstractIntVector dims = Utils.<RAbstractIntVector> castTo(
-                        s.getAttr(dummyAttrProfiles, RRuntime.DIM_ATTR_KEY));
+                        s.getAttr(RRuntime.DIM_ATTR_KEY));
 
         final int ns = s.getLength();
 
@@ -82,7 +79,7 @@ final class PairListPrinter extends AbstractValuePrinter<RPairList> {
                 t[i] = pbuf;
 
                 RStringVector tt = RDataFactory.createStringVector(t, true, s.getDimensions());
-                Object dimNames = s.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY);
+                Object dimNames = s.getAttr(RRuntime.DIMNAMES_ATTR_KEY);
                 tt.setAttr(RRuntime.DIMNAMES_ATTR_KEY, dimNames);
 
                 PrintContext cc = printCtx.cloneContext();
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
index cbcb5e2af91c4a3884b2f87f57ca61a10498684b..7448ebc7516c8ea623afd1c3ca912f86528212e5 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/ValuePrinterNode.java
@@ -542,7 +542,7 @@ public final class ValuePrinterNode extends RBaseNode {
         }
 
         @Override
-        public void setNames(RStringVector newNames) {
+        public final void setNames(RStringVector newNames) {
             throw RInternalError.shouldNotReachHere();
         }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java
index dcbc682204eda47037bf0d17c584f364f162973f..5577dd7c283cd4f934d3edcdee26c1a71687af24 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/VectorPrinter.java
@@ -19,7 +19,6 @@ import java.io.PrintWriter;
 
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
@@ -29,8 +28,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
 abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePrinter<T> {
 
-    private static RAttributeProfiles dummyAttrProfiles = RAttributeProfiles.create();
-
     @Override
     protected void printValue(T vector, PrintContext printCtx) throws IOException {
         printVector(vector, 1, printCtx);
@@ -77,13 +74,13 @@ abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePri
 
             MatrixDimNames mdn = null;
 
-            Object dimAttr = vector.getAttr(dummyAttrProfiles, RRuntime.DIM_ATTR_KEY);
+            Object dimAttr = vector.getAttr(RRuntime.DIM_ATTR_KEY);
             if (dimAttr instanceof RAbstractIntVector) {
                 dims = (RAbstractIntVector) dimAttr;
                 if (dims.getLength() == 1) {
-                    RList t = Utils.<RList> castTo(vector.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY));
+                    RList t = Utils.<RList> castTo(vector.getAttr(RRuntime.DIMNAMES_ATTR_KEY));
                     if (t != null && t.getDataAt(0) != null) {
-                        RAbstractStringVector nn = Utils.castTo(RRuntime.asAbstractVector(t.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY)));
+                        RAbstractStringVector nn = Utils.castTo(RRuntime.asAbstractVector(t.getNames()));
 
                         if (nn != null) {
                             title = nn.getDataAt(0);
@@ -111,7 +108,7 @@ abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePri
                 }
             } else {
                 dims = null;
-                Object namesAttr = Utils.castTo(vector.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY));
+                Object namesAttr = Utils.castTo(vector.getAttr(RRuntime.NAMES_ATTR_KEY));
                 if (namesAttr != null) {
                     if (vector.getLength() > 0) {
                         names = Utils.castTo(RRuntime.asAbstractVector(namesAttr));
@@ -676,7 +673,7 @@ abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePri
         final RAbstractStringVector axisNames;
 
         MatrixDimNames(RAbstractVector x) {
-            dimnames = Utils.<RList> castTo(x.getAttr(dummyAttrProfiles, RRuntime.DIMNAMES_ATTR_KEY));
+            dimnames = Utils.<RList> castTo(x.getAttr(RRuntime.DIMNAMES_ATTR_KEY));
 
             if (dimnames == null) {
                 rl = null;
@@ -688,7 +685,7 @@ abstract class VectorPrinter<T extends RAbstractVector> extends AbstractValuePri
             } else {
                 rl = getDimNamesAt(0);
                 cl = getDimNamesAt(1);
-                axisNames = Utils.<RAbstractStringVector> castTo(dimnames.getAttr(dummyAttrProfiles, RRuntime.NAMES_ATTR_KEY));
+                axisNames = Utils.<RAbstractStringVector> castTo(dimnames.getNames());
                 if (axisNames == null) {
                     rn = null;
                     cn = null;
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
index c739ffe80e385fe6d6f4ea1f1241ffe44e63e9eb..714e8c2a381a8dd49dd583944e2439d4e5d3ef7d 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastrDqrls.java
@@ -23,7 +23,6 @@ import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RList;
@@ -44,10 +43,6 @@ public abstract class FastrDqrls extends RBuiltinNode {
     private static final String[] NAMES = new String[]{"qr", "coefficients", "residuals", "effects", "rank", "pivot", "qraux", "tol", "pivoted"};
     private static RStringVector namesVector = null;
 
-    private final RAttributeProfiles coeffAttributeProfiles = RAttributeProfiles.create();
-    private final RAttributeProfiles xAttributeProfiles = RAttributeProfiles.create();
-    private final RAttributeProfiles residualsAttributesProfiles = RAttributeProfiles.create();
-
     @Override
     protected void createCasts(CastBuilder casts) {
         casts.arg("x").asDoubleVector(true, true, true);
@@ -87,13 +82,13 @@ public abstract class FastrDqrls extends RBuiltinNode {
         rApplRFFINode.dqrls(x, n, p, y, ny, tol, coeff, residuals, effects, rank, pivot, qraux, work);
 
         RDoubleVector resultCoeffVect = RDataFactory.createDoubleVector(coeff, RDataFactory.COMPLETE_VECTOR);
-        resultCoeffVect.copyAttributesFrom(coeffAttributeProfiles, coeffVec);
+        resultCoeffVect.copyAttributesFrom(coeffVec);
 
         RDoubleVector resultX = RDataFactory.createDoubleVector(x, RDataFactory.COMPLETE_VECTOR);
-        resultX.copyAttributesFrom(xAttributeProfiles, originalXVec);
+        resultX.copyAttributesFrom(originalXVec);
 
         RDoubleVector resultResiduals = RDataFactory.createDoubleVector(residuals, RDataFactory.COMPLETE_VECTOR);
-        resultResiduals.copyAttributesFrom(residualsAttributesProfiles, originalYVec);
+        resultResiduals.copyAttributesFrom(originalYVec);
 
         Object[] data = new Object[]{
                         resultX,
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
index 044773513f6598af456b3be185fda9108d69737c..78c4fd48204f108f6fb44bc9d9ff2eaa70ff7f1c 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java
@@ -27,6 +27,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.MaterializedFrame;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RCaller;
@@ -69,6 +70,9 @@ public abstract class BrowserInteractNode extends RNode {
     public static final int CONTINUE = 2;
     public static final int FINISH = 3;
 
+    @Child private GetFixedAttributeNode getSrcRefAttrNode;
+    @Child private GetFixedAttributeNode getSrcFileAttrNode;
+
     @Specialization
     protected int interact(VirtualFrame frame) {
         CompilerDirectives.transferToInterpreter();
@@ -179,11 +183,20 @@ public abstract class BrowserInteractNode extends RNode {
         return RCaller.create(null, currentCaller, builder.call(RSyntaxNode.INTERNAL, builder.lookup(RSyntaxNode.INTERNAL, "browser", true)));
     }
 
-    private static String getSrcinfo(RStringVector element) {
-        Object srcref = element.getAttr(RRuntime.R_SRCREF);
+    private String getSrcinfo(RStringVector element) {
+        if (getSrcRefAttrNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            getSrcRefAttrNode = insert(GetFixedAttributeNode.create(RRuntime.R_SRCREF));
+        }
+
+        Object srcref = getSrcRefAttrNode.execute(element);
         if (srcref != null) {
+            if (getSrcFileAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getSrcFileAttrNode = insert(GetFixedAttributeNode.create(RRuntime.R_SRCFILE));
+            }
             RIntVector lloc = (RIntVector) srcref;
-            Object srcfile = lloc.getAttr(RRuntime.R_SRCFILE);
+            Object srcfile = getSrcFileAttrNode.execute(lloc);
             if (srcfile != null) {
                 REnvironment env = (REnvironment) srcfile;
                 return " at " + RRuntime.asString(env.get(RSrcref.SrcrefFields.filename.name())) + "#" + lloc.getDataAt(0);
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
index d17626bd61f79b6c3f08b544914cebc5fdca54c1..b0e55a5d0192eea5ba7efba7987f15b82d589f6f 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/builtin/CastBuilderTest.java
@@ -64,7 +64,6 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.util.function.Consumer;
 import java.util.function.Function;
 
 import org.junit.After;
@@ -75,7 +74,6 @@ import org.junit.Test;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
 import com.oracle.truffle.r.nodes.builtin.casts.PipelineConfig;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.InitialPhaseBuilder;
-import com.oracle.truffle.r.nodes.builtin.casts.fluent.PipelineConfigBuilder;
 import com.oracle.truffle.r.nodes.builtin.casts.fluent.PreinitialPhaseBuilder;
 import com.oracle.truffle.r.nodes.casts.CastNodeSampler;
 import com.oracle.truffle.r.nodes.casts.FilterSamplerFactory;
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/BinaryArithmeticNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/BinaryArithmeticNodeTest.java
index 668afbfc228cf49e199438f87a448f9b7ef0b88f..ce538982b09d76218ae36524e3cb873e9f26cc20 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/BinaryArithmeticNodeTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/BinaryArithmeticNodeTest.java
@@ -67,7 +67,6 @@ import com.oracle.truffle.r.nodes.binary.BinaryArithmeticNode;
 import com.oracle.truffle.r.nodes.test.TestUtilities.NodeHandle;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RScalarVector;
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/UnaryArithmeticNodeTest.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/UnaryArithmeticNodeTest.java
index d231d368106ff91a044eb2fe90e2f943d1213b0c..5a7ef2b6e1eeb41b379c803ec9e26d2326f66b67 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/UnaryArithmeticNodeTest.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/UnaryArithmeticNodeTest.java
@@ -56,7 +56,6 @@ import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticNodeGen;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RScalarVector;
 import com.oracle.truffle.r.runtime.data.RSequence;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
index 9013ee971727f511d890c57a113796f664921721..589acd316b992ddc87812d2e6fb7306c88c4562d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedExtractVectorNode.java
@@ -33,7 +33,10 @@ import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.vector.CachedExtractVectorNodeFactory.SetNamesNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
-import com.oracle.truffle.r.nodes.attributes.SetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.nodes.profile.AlwaysOnBranchProfile;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RError;
@@ -69,11 +72,15 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     private final boolean dropDimensions;
 
     private final VectorLengthProfile vectorLengthProfile = VectorLengthProfile.create();
-    private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
 
     @Child private WriteIndexedVectorNode writeVectorNode;
     @Child private PositionsCheckNode positionsCheckNode;
     @Child private SetNamesNode setNamesNode;
+    @Child private SetDimAttributeNode setDimNode;
+    @Child private SetDimNamesAttributeNode setDimNamesNode;
+    @Child private GetDimNamesAttributeNode getDimNamesNode;
+    @Child private GetNamesAttributeNode getNamesNode;
+    @Child private GetNamesAttributeNode getNamesFromDimNamesNode;
     @Children private final CachedExtractVectorNode[] extractNames;
     @Children private final CachedExtractVectorNode[] extractNamesAlternative;
 
@@ -185,7 +192,11 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             }
             if (oneDimensionProfile.profile(numberOfDimensions == 1)) {
                 // names only need to be considered for single dimensional accesses
-                RStringVector originalNames = vector.getNames(vectorNamesProfile);
+                if (getNamesNode == null) {
+                    CompilerDirectives.transferToInterpreter();
+                    getNamesNode = insert(GetNamesAttributeNode.create());
+                }
+                RStringVector originalNames = getNamesNode.getNames(vector);
                 if (originalNames != null) {
                     metadataApplied.enter();
                     setNames(extractedVector, extractNames(originalNames, positions, positionProfiles, 0, originalExact, originalDropDimensions));
@@ -274,7 +285,6 @@ final class CachedExtractVectorNode extends CachedVectorNode {
     }
 
     private final ConditionProfile dimNamesNull = ConditionProfile.createBinaryProfile();
-    private final RAttributeProfiles dimnamesNamesProfile = RAttributeProfiles.create();
     private final ValueProfile foundDimNamesProfile = ValueProfile.createClassProfile();
     private final ConditionProfile selectPositionsProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile originalDimNamesPRofile = ConditionProfile.createBinaryProfile();
@@ -285,8 +295,13 @@ final class CachedExtractVectorNode extends CachedVectorNode {
         // TODO speculate on the number of counted dimensions
         int dimCount = countDimensions(positionProfile);
 
+        if (getDimNamesNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            getDimNamesNode = insert(GetDimNamesAttributeNode.create());
+        }
+
         int[] newDimensions = new int[dimCount];
-        RList originalDimNames = originalTarget.getDimNames(null);
+        RList originalDimNames = getDimNamesNode.getDimNames(originalTarget);
         RStringVector originalDimNamesNames;
         Object[] newDimNames;
         String[] newDimNamesNames;
@@ -295,8 +310,12 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             originalDimNamesNames = null;
             newDimNamesNames = null;
         } else {
+            if (getNamesFromDimNamesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getNamesFromDimNamesNode = insert(GetNamesAttributeNode.create());
+            }
             newDimNames = new Object[dimCount];
-            originalDimNamesNames = originalDimNames.getNames(dimnamesNamesProfile);
+            originalDimNamesNames = getNamesFromDimNamesNode.getNames(originalDimNames);
             newDimNamesNames = originalDimNamesNames == null ? null : new String[dimCount];
         }
 
@@ -326,9 +345,20 @@ final class CachedExtractVectorNode extends CachedVectorNode {
 
         if (resultHasDimensions.profile(dimCount > 1)) {
             metadataApplied.enter();
-            extractedTarget.setDimensions(newDimensions);
+
+            if (setDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setDimNode = insert(SetDimAttributeNode.create());
+            }
+
+            setDimNode.setDimensions(extractedTarget, newDimensions);
             if (newDimNames != null) {
-                extractedTarget.setDimNames(RDataFactory.createList(newDimNames, newDimNamesNames == null ? null : RDataFactory.createStringVector(newDimNamesNames, originalDimNames.isComplete())));
+                if (setDimNamesNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setDimNamesNode = insert(SetDimNamesAttributeNode.create());
+                }
+                setDimNamesNode.setDimNames(extractedTarget,
+                                RDataFactory.createList(newDimNames, newDimNamesNames == null ? null : RDataFactory.createStringVector(newDimNamesNames, originalDimNames.isComplete())));
             }
         } else if (newDimNames != null && originalDimNamesPRofile.profile(originalDimNames.getLength() > 0)) {
             RAbstractStringVector foundNames = translateDimNamesToNames(positionProfile, originalDimNames, extractedTargetLength, positions);
@@ -445,7 +475,6 @@ final class CachedExtractVectorNode extends CachedVectorNode {
             if (container.getAttributes() == null) {
                 // usual case
                 container.initAttributes(RAttributesLayout.createNames(newNames1));
-                container.setInternalNames(newNames1);
             } else {
                 // from an RLanguage extraction that set a name
                 RStringVector oldNames = (RStringVector) namesAttrGetter.execute(container.getAttributes());
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
index e951c3c9927316606423f3d313152a0bc252a6c4..2113155262a366925b79e1963772fda9a23f2806 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedReplaceVectorNode.java
@@ -40,6 +40,8 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.vector.CachedReplaceVectorNodeFactory.ValueProfileNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.nodes.binary.CastTypeNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.nodes.unary.CastListNodeGen;
@@ -79,7 +81,6 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
     private final VectorLengthProfile valueLengthProfile = VectorLengthProfile.create();
     private final BranchProfile warningBranch = BranchProfile.create();
     private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
-    private final RAttributeProfiles positionNamesProfile = RAttributeProfiles.create();
     private final ConditionProfile valueIsNA = ConditionProfile.createBinaryProfile();
     private final BranchProfile resizeProfile = BranchProfile.create();
     private final BranchProfile sharedProfile = BranchProfile.create();
@@ -100,6 +101,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
     @Child private CastNode castVectorNode;
     @Child private CachedReplaceVectorNode copyPositionNames;
     @Child private DeleteElementsNode deleteElementsNode;
+    @Child private SetNamesAttributeNode setNamesNode;
 
     CachedReplaceVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, RTypedValue value, boolean updatePositionNames, boolean recursive) {
         super(mode, vector, positions, recursive);
@@ -438,6 +440,8 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
     }
 
     private final ValueProfile positionCastProfile = ValueProfile.createClassProfile();
+    @Child private GetNamesAttributeNode getNamesNode;
+    @Child private GetNamesAttributeNode getResultNamesNode;
 
     private void updateVectorWithPositionNames(RAbstractVector vector, Object[] positions) {
         Object position = positionCastProfile.profile(positions[0]);
@@ -446,7 +450,11 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         if (position instanceof RMissing) {
             positionNames = null;
         } else {
-            positionNames = ((RAbstractVector) position).getNames(positionNamesProfile);
+            if (getNamesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getNamesNode = insert(GetNamesAttributeNode.create());
+            }
+            positionNames = getNamesNode.getNames(position);
         }
         if (positionNames != null && positionNames.getLength() > 0) {
             updatePositionNames(vector, positionNames, positions);
@@ -537,7 +545,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         RStringVector oldNames = vector.getNames(vectorNamesProfile);
         RVector<?> res = vector.copyResized(size, true).materialize();
         if (vector instanceof RVector) {
-            res.copyAttributesFrom(positionNamesProfile, vector);
+            res.copyAttributesFrom(vector);
         }
         res.setDimensionsNoCheck(null);
         res.setDimNamesNoCheck(null);
@@ -551,7 +559,11 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
     private final ConditionProfile updateNamesProfile = ConditionProfile.createBinaryProfile();
 
     private void updatePositionNames(RAbstractVector resultVector, RAbstractStringVector positionNames, Object[] positions) {
-        RTypedValue originalNames = resultVector.getNames(positionNamesProfile);
+        if (getResultNamesNode == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            getResultNamesNode = insert(GetNamesAttributeNode.create());
+        }
+        RTypedValue originalNames = getResultNamesNode.getNames(resultVector);
         RTypedValue names = originalNames;
         if (names == null) {
             String[] emptyVector = new String[resultVector.getLength()];
@@ -565,7 +577,11 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
         assert copyPositionNames.isSupported(names, positions, positionNames);
         RAbstractStringVector newNames = (RAbstractStringVector) copyPositionNames.apply(names, positions, positionNames);
         if (updateNamesProfile.profile(newNames != originalNames)) {
-            resultVector.setNames(newNames.materialize());
+            if (setNamesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setNamesNode = insert(SetNamesAttributeNode.create());
+            }
+            setNamesNode.setNames(resultVector, newNames.materialize());
         }
     }
 
@@ -580,7 +596,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
          */
         @CompilationFinal private int previousResultLength = PREVIOUS_RESULT_UNINITIALIZED;
 
-        private final RAttributeProfiles vectorNamesProfile = RAttributeProfiles.create();
+        @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
         public RAbstractVector deleteElements(RAbstractVector vector, int vectorLength) {
             // we can speculate here that we delete always the same number of elements
@@ -596,7 +612,7 @@ final class CachedReplaceVectorNode extends CachedVectorNode {
             }
 
             Object[] data = new Object[resultLength];
-            RStringVector names = vector.getNames(vectorNamesProfile);
+            RStringVector names = getNamesNode.getNames(vector);
             boolean hasNames = names != null;
             String[] newNames = null;
             if (hasNames) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
index 2622dfc83ebb23d9920416d187264533e4ce06fe..ddd61957c7c5e09b7df428161eff08cd00e3ace4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/CachedVectorNode.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.access.vector;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
@@ -60,6 +61,8 @@ abstract class CachedVectorNode extends RBaseNode {
     // if this is non-null, the node needs to throw the error whenever it is executed
     @CompilationFinal protected Runnable error;
 
+    @Child private GetDimAttributeNode getDimNode = GetDimAttributeNode.create();
+
     CachedVectorNode(ElementAccessMode mode, RTypedValue vector, Object[] positions, boolean recursive) {
         this.mode = mode;
         this.vectorType = vector.getRType();
@@ -151,14 +154,13 @@ abstract class CachedVectorNode extends RBaseNode {
         }
     }
 
-    @SuppressWarnings("static-method")
     protected final int[] loadVectorDimensions(RAbstractContainer vector) {
         // N.B. (stepan) this method used to be instance method and have special handling for
         // RDataFrame, which was removed and any test case, which would require this special
         // handling was not found (see TestBuiltin_extract_dataframe for tests used and further
         // explanation). This method and note will remain here for a while in case this behavior
         // crops up somewhere
-        return vector.getDimensions();
+        return getDimNode.getDimensions(vector);
     }
 
     public ElementAccessMode getMode() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java
index e437eef25787fc36433b988c3109a99dcd04ba6b..70e086e85722e0917405de4db7f6f82244cd5526 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCharacterLookupNode.java
@@ -24,9 +24,10 @@ package com.oracle.truffle.r.nodes.access.vector;
 
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -38,12 +39,13 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 final class PositionCharacterLookupNode extends Node {
 
     private final ElementAccessMode mode;
-    private final RAttributeProfiles attributeProfiles = RAttributeProfiles.create();
     private final int numDimensions;
     private final int dimensionIndex;
     private final BranchProfile emptyProfile = BranchProfile.create();
 
     @Child private SearchFirstStringNode searchNode;
+    @Child private GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     PositionCharacterLookupNode(ElementAccessMode mode, int numDimensions, int dimensionIndex, boolean useNAForNotFound, boolean exact) {
         this.numDimensions = numDimensions;
@@ -56,14 +58,14 @@ final class PositionCharacterLookupNode extends Node {
         // lookup names for single dimension case
         RAbstractIntVector result;
         if (numDimensions <= 1) {
-            RStringVector names = target.getNames(attributeProfiles);
+            RStringVector names = getNamesNode.getNames(target);
             if (names == null) {
                 emptyProfile.enter();
                 names = RDataFactory.createEmptyStringVector();
             }
             result = searchNode.apply(names, position, notFoundStartIndex, position.materialize());
         } else {
-            RList dimNames = target.getDimNames(attributeProfiles);
+            RList dimNames = getDimNamesNode.getDimNames(target);
             if (dimNames != null) {
                 Object dataAt = dimNames.getDataAt(dimensionIndex);
                 if (dataAt != RNull.instance) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
index 5ec428e5feefa3341125319e430de02631d25336..9dffa7e5111fdd7459b8c5f77352b2daa85eed54 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckNode.java
@@ -30,6 +30,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.vector.PositionCheckNodeFactory.Mat2indsubNodeGen;
 import com.oracle.truffle.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.control.RLengthNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RError;
@@ -110,6 +111,8 @@ abstract class PositionCheckNode extends Node {
     }
 
     @Child private Mat2indsubNode mat2indsub;
+    @Child private GetDimAttributeNode getVectorDimsNode;
+    @Child private GetDimAttributeNode getVectorPosDimsNode;
 
     public final Object execute(PositionProfile profile, RAbstractContainer vector, int[] vectorDimensions, int vectorLength, Object position) {
         Object castPosition = castNode.execute(positionClass.cast(position));
@@ -124,12 +127,20 @@ abstract class PositionCheckNode extends Node {
         }
 
         if (mode.isSubset() && numDimensions == 1) {
-            int[] vectorDim = vector.getDimensions();
+            if (getVectorDimsNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getVectorDimsNode = insert(GetDimAttributeNode.create());
+            }
+            int[] vectorDim = getVectorDimsNode.getDimensions(vector);
             if (nullDimensionsProfile.profile(vectorDim != null) && vectorDim.length == 2) {
                 if (vector instanceof RAbstractVector && ((RAbstractVector) vector).isArray()) {
                     if (castPosition instanceof RAbstractVector) {
+                        if (getVectorPosDimsNode == null) {
+                            CompilerDirectives.transferToInterpreterAndInvalidate();
+                            getVectorPosDimsNode = insert(GetDimAttributeNode.create());
+                        }
                         RAbstractVector vectorPosition = (RAbstractVector) castPosition;
-                        int[] posDim = vectorPosition.getDimensions();
+                        int[] posDim = getVectorPosDimsNode.getDimensions(vectorPosition);
                         if (posDim != null && posDim.length == 2 && posDim[1] == vectorDim.length) {
                             if (castPosition instanceof RAbstractIntVector || castPosition instanceof RAbstractDoubleVector) {
                                 if (mat2indsub == null) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java
index 43486bf76a3b037d6e5eaf5ab11043ceba180ece..6aa4578656348d2c47e4ea9f19d82418716a06af 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubscriptNode.java
@@ -22,9 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.access.vector;
 
+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.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -61,7 +63,8 @@ abstract class PositionCheckSubscriptNode extends PositionCheckNode {
     }
 
     @Specialization
-    protected RAbstractVector doLogical(PositionProfile statistics, int dimSize, RAbstractLogicalVector position, int positionLength) {
+    protected RAbstractVector doLogical(PositionProfile statistics, int dimSize, RAbstractLogicalVector position, int positionLength,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         positionNACheck.enable(position);
         byte value = position.getDataAt(0);
         if (positionLength != 1) {
@@ -77,11 +80,12 @@ abstract class PositionCheckSubscriptNode extends PositionCheckNode {
             }
         }
 
-        return doIntegerImpl(statistics, dimSize, positionNACheck.convertLogicalToInt(value), position);
+        return doIntegerImpl(statistics, dimSize, positionNACheck.convertLogicalToInt(value), position, getNamesNode);
     }
 
     @Specialization
-    protected RAbstractVector doInteger(PositionProfile profile, int dimSize, RAbstractIntVector position, int positionLength) {
+    protected RAbstractVector doInteger(PositionProfile profile, int dimSize, RAbstractIntVector position, int positionLength,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         if (positionLength != 1) {
             error.enter();
             Message message;
@@ -99,12 +103,12 @@ abstract class PositionCheckSubscriptNode extends PositionCheckNode {
         }
         assert positionLength == 1;
         positionNACheck.enable(position);
-        RAbstractVector result = doIntegerImpl(profile, dimSize, position.getDataAt(0), position);
+        RAbstractVector result = doIntegerImpl(profile, dimSize, position.getDataAt(0), position, getNamesNode);
         return result;
 
     }
 
-    private RAbstractVector doIntegerImpl(PositionProfile profile, int dimSize, int value, RAbstractVector originalVector) {
+    private RAbstractVector doIntegerImpl(PositionProfile profile, int dimSize, int value, RAbstractVector originalVector, GetNamesAttributeNode getNamesNode) {
         int result;
         if (greaterZero.profile(value > 0)) {
             // fast path
@@ -121,7 +125,7 @@ abstract class PositionCheckSubscriptNode extends PositionCheckNode {
         }
         profile.selectedPositionsCount = 1;
 
-        RStringVector names = originalVector.getNames(attributeProfile);
+        RStringVector names = getNamesNode.getNames(originalVector);
         if (names != null) {
             return RDataFactory.createIntVector(new int[]{result}, !profile.containsNA, names);
         } else {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java
index bc29b18d2a136d3ebb10508126a66727b3b771a0..99ad2b78cab1e9f3630e33e9a8c1345da56e6735 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/vector/PositionCheckSubsetNode.java
@@ -31,12 +31,13 @@ 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.r.nodes.access.vector.PositionsCheckNode.PositionProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.NullProfile;
 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.Utils;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RInteger;
 import com.oracle.truffle.r.runtime.data.RMissing;
@@ -128,8 +129,6 @@ abstract class PositionCheckSubsetNode extends PositionCheckNode {
         return position;
     }
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     @Specialization(/* contains = "doSequence" */)
     protected RAbstractVector doDouble(PositionProfile profile, int dimensionLength, RAbstractDoubleVector position, int positionLength, //
                     @Cached("create()") BranchProfile seenZeroProfile, //
@@ -137,13 +136,15 @@ abstract class PositionCheckSubsetNode extends PositionCheckNode {
                     @Cached("create()") BranchProfile seenNegativeProfile, //
                     @Cached("create()") BranchProfile seenOutOfBounds, //
                     @Cached("create()") NullProfile hasNamesProfile, //
-                    @Cached("createCountingProfile()") LoopConditionProfile lengthProfile) {
+                    @Cached("createCountingProfile()") LoopConditionProfile lengthProfile,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode,
+                    @Cached("create()") SetNamesAttributeNode setNamesNode) {
         RAbstractIntVector intPosition = RDataFactory.createIntVector(positionLength);
         intPosition.setComplete(position.isComplete());
         // requires names preservation
-        RStringVector names = hasNamesProfile.profile(position.getNames(attrProfiles));
+        RStringVector names = hasNamesProfile.profile(getNamesNode.getNames(position));
         if (names != null) {
-            intPosition.setNames(names);
+            setNamesNode.setNames(intPosition, names);
         }
         Object convertedStore = intPosition.getInternalStore();
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
index e1f30141ef54d7abc96f2dc4f0f5a5133c81f677..78faca3c4d6bfe0b214b34a5ee689421b0ccad47 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
@@ -28,8 +28,11 @@ 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.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.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -47,8 +50,9 @@ public abstract class CopyAttributesNode extends RBaseNode {
 
     private final boolean copyAllAttributes;
 
-    protected final RAttributeProfiles attrLeftProfiles = RAttributeProfiles.create();
-    protected final RAttributeProfiles attrRightProfiles = RAttributeProfiles.create();
+    @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+    @Child protected GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
+    @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     protected CopyAttributesNode(boolean copyAllAttributes) {
         this.copyAllAttributes = copyAllAttributes;
@@ -60,9 +64,9 @@ public abstract class CopyAttributesNode extends RBaseNode {
 
     public abstract RAbstractVector execute(RAbstractVector target, RAbstractVector left, int leftLength, RAbstractVector right, int rightLength);
 
-    protected boolean containsMetadata(RAbstractVector vector, RAttributeProfiles attrProfiles) {
-        return vector instanceof RVector && vector.hasDimensions() || (copyAllAttributes && vector.getAttributes() != null) || vector.getDimNames(attrProfiles) != null ||
-                        vector.getNames(attrProfiles) != null;
+    protected boolean containsMetadata(RAbstractVector vector) {
+        return vector instanceof RVector && hasDimNode.execute(vector) || (copyAllAttributes && vector.getAttributes() != null) || getDimNamesNode.getDimNames(vector) != null ||
+                        getNamesNode.getNames(vector) != null;
     }
 
     private static int countNo;
@@ -98,7 +102,7 @@ public abstract class CopyAttributesNode extends RBaseNode {
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = {"!containsMetadata(left, attrLeftProfiles)", "!containsMetadata(right, attrRightProfiles)"})
+    @Specialization(guards = {"!containsMetadata(left)", "!containsMetadata(right)"})
     protected RAbstractVector copyNoMetadata(RAbstractVector target, RAbstractVector left, int leftLength, RAbstractVector right, int rightLength) {
         if (LOG) {
             log("copyAttributes: no");
@@ -107,7 +111,7 @@ public abstract class CopyAttributesNode extends RBaseNode {
         return target;
     }
 
-    @Specialization(guards = {"leftLength == rightLength", "containsMetadata(left, attrLeftProfiles) || containsMetadata(right, attrRightProfiles)"})
+    @Specialization(guards = {"leftLength == rightLength", "containsMetadata(left) || containsMetadata(right)"})
     protected RAbstractVector copySameLength(RAbstractVector target, RAbstractVector left, @SuppressWarnings("unused") int leftLength, RAbstractVector right,
                     @SuppressWarnings("unused") int rightLength,
                     @Cached("create()") CopyOfRegAttributesNode copyOfRegLeft,
@@ -124,7 +128,10 @@ public abstract class CopyAttributesNode extends RBaseNode {
                     @Cached("create()") BranchProfile noDimensions,
                     @Cached("createBinaryProfile()") ConditionProfile hasNamesLeft,
                     @Cached("createBinaryProfile()") ConditionProfile hasNamesRight,
-                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames) {
+                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames,
+                    @Cached("create()") GetDimAttributeNode getLeftDimsNode,
+                    @Cached("create()") GetDimAttributeNode getRightDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode) {
         if (LOG) {
             log("copyAttributes: ==");
             countEquals++;
@@ -139,32 +146,28 @@ public abstract class CopyAttributesNode extends RBaseNode {
             }
         }
 
-        int[] newDimensions = left.getDimensions();
+        int[] newDimensions = getLeftDimsNode.getDimensions(left);
         if (newDimensions == null) {
-            newDimensions = right.getDimensions();
+            newDimensions = getRightDimsNode.getDimensions(right);
             if (newDimensions == null) {
                 noDimensions.enter();
                 DynamicObject attributes = result.getAttributes();
                 if (hasAttributes.profile(attributes != null)) {
                     removeDim.execute(attributes);
                     removeDimNames.execute(attributes);
-                    result.setInternalDimNames(null);
                 }
-                result.setInternalDimensions(null);
 
-                RStringVector vecNames = left.getNames(attrLeftProfiles);
+                RStringVector vecNames = getNamesNode.getNames(left);
                 if (hasNamesLeft.profile(vecNames != null)) {
                     if (result != left) {
                         putNames.execute(initAttributes.execute(result), vecNames);
-                        result.setInternalNames(vecNames);
                     }
                     return result;
                 }
                 if (result != right) {
-                    vecNames = right.getNames(attrRightProfiles);
+                    vecNames = getNamesNode.getNames(right);
                     if (hasNamesRight.profile(vecNames != null)) {
                         putNames.execute(initAttributes.execute(result), vecNames);
-                        result.setInternalNames(vecNames);
                     }
                 }
                 return result;
@@ -176,28 +179,26 @@ public abstract class CopyAttributesNode extends RBaseNode {
         }
 
         putDim.execute(initAttributes.execute(result), RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
-        result.setInternalDimensions(newDimensions);
 
         if (result != left) {
-            RList newDimNames = left.getDimNames(attrLeftProfiles);
+            RList newDimNames = getDimNamesNode.getDimNames(left);
             if (hasDimNames.profile(newDimNames != null)) {
                 putDimNames.execute(result.getAttributes(), newDimNames);
 
                 newDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
-                result.setInternalDimNames(newDimNames);
                 return result;
             }
             if (result != right) {
-                newDimNames = right.getDimNames(attrRightProfiles);
+                newDimNames = getDimNamesNode.getDimNames(right);
                 if (hasDimNames.profile(newDimNames != null)) {
-                    result.setDimNames(newDimNames);
+                    setDimNamesNode.setDimNames(result, newDimNames);
                 }
             }
         }
         return result;
     }
 
-    @Specialization(guards = {"leftLength < rightLength", "containsMetadata(left, attrLeftProfiles) || containsMetadata(right, attrRightProfiles)"})
+    @Specialization(guards = {"leftLength < rightLength", "containsMetadata(left) || containsMetadata(right)"})
     protected RAbstractVector copyShorter(RAbstractVector target, RAbstractVector left, @SuppressWarnings("unused") int leftLength, RAbstractVector right, @SuppressWarnings("unused") int rightLength, //
                     @Cached("create()") CopyOfRegAttributesNode copyOfReg, //
                     @Cached("createBinaryProfile()") ConditionProfile rightNotResultProfile, //
@@ -208,7 +209,10 @@ public abstract class CopyAttributesNode extends RBaseNode {
                     @Cached("createDim()") SetFixedAttributeNode putDim, //
                     @Cached("create()") InitAttributesNode initAttributes, //
                     @Cached("createBinaryProfile()") ConditionProfile hasNames, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames) {
+                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames,
+                    @Cached("create()") GetDimAttributeNode getLeftDimsNode,
+                    @Cached("create()") GetDimAttributeNode getRightDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode) {
         if (LOG) {
             log("copyAttributes: <");
             countSmaller++;
@@ -219,18 +223,17 @@ public abstract class CopyAttributesNode extends RBaseNode {
             copyOfReg.execute(right, result);
         }
 
-        int[] newDimensions = left.getDimensions();
+        int[] newDimensions = getLeftDimsNode.getDimensions(left);
         if (newDimensions == null || (newDimensions.length == 2 && newDimensions[0] == 1 && newDimensions[1] == 1)) {
             // 1-element matrix should be treated as 1-element vector
-            newDimensions = right.getDimensions();
+            newDimensions = getRightDimsNode.getDimensions(right);
             if (newDimensions == null || (newDimensions.length == 2 && newDimensions[0] == 1 && newDimensions[1] == 1)) {
                 // 1-element matrix should be treated as 1-element vector
                 noDimensions.enter();
                 if (rightNotResult) {
-                    RStringVector vecNames = right.getNames(attrRightProfiles);
+                    RStringVector vecNames = getNamesNode.getNames(right);
                     if (hasNames.profile(vecNames != null)) {
                         putNames.execute(initAttributes.execute(result), vecNames);
-                        result.setInternalNames(vecNames);
                     }
                 }
                 return result;
@@ -243,17 +246,16 @@ public abstract class CopyAttributesNode extends RBaseNode {
 
         RVector.verifyDimensions(result.getLength(), newDimensions, this);
         putDim.execute(initAttributes.execute(result), RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
-        result.setInternalDimensions(newDimensions);
         if (rightNotResult) {
-            RList newDimNames = right.getDimNames(attrRightProfiles);
+            RList newDimNames = getDimNamesNode.getDimNames(right);
             if (hasDimNames.profile(newDimNames != null)) {
-                result.setDimNames(newDimNames);
+                setDimNamesNode.setDimNames(result, newDimNames);
             }
         }
         return result;
     }
 
-    @Specialization(guards = {"leftLength > rightLength", "containsMetadata(left, attrLeftProfiles) || containsMetadata(right, attrRightProfiles)"})
+    @Specialization(guards = {"leftLength > rightLength", "containsMetadata(left) || containsMetadata(right)"})
     protected RAbstractVector copyLonger(RAbstractVector target, RAbstractVector left, @SuppressWarnings("unused") int leftLength, RAbstractVector right, @SuppressWarnings("unused") int rightLength, //
                     @Cached("create()") CopyOfRegAttributesNode copyOfReg, //
                     @Cached("create()") BranchProfile leftHasDimensions, //
@@ -263,7 +265,10 @@ public abstract class CopyAttributesNode extends RBaseNode {
                     @Cached("createDim()") SetFixedAttributeNode putDim, //
                     @Cached("create()") InitAttributesNode initAttributes, //
                     @Cached("createBinaryProfile()") ConditionProfile hasNames, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames) {
+                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames,
+                    @Cached("create()") GetDimAttributeNode getLeftDimsNode,
+                    @Cached("create()") GetDimAttributeNode getRightDimsNode,
+                    @Cached("create()") SetDimNamesAttributeNode setDimNamesNode) {
         if (LOG) {
             log("copyAttributes: >");
             countLarger++;
@@ -272,16 +277,15 @@ public abstract class CopyAttributesNode extends RBaseNode {
         if (copyAllAttributes && result != left) {
             copyOfReg.execute(left, result);
         }
-        int[] newDimensions = left.getDimensions();
+        int[] newDimensions = getLeftDimsNode.getDimensions(left);
         if (newDimensions == null) {
-            newDimensions = right.getDimensions();
+            newDimensions = getRightDimsNode.getDimensions(right);
             if (newDimensions == null) {
                 noDimensions.enter();
                 if (left != result) {
-                    RStringVector vecNames = left.getNames(attrLeftProfiles);
+                    RStringVector vecNames = getNamesNode.getNames(left);
                     if (hasNames.profile(vecNames != null)) {
                         putNames.execute(initAttributes.execute(result), vecNames);
-                        result.setInternalNames(vecNames);
                     }
                 }
                 return result;
@@ -292,11 +296,10 @@ public abstract class CopyAttributesNode extends RBaseNode {
             leftHasDimensions.enter();
         }
         putDim.execute(initAttributes.execute(result), RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
-        result.setInternalDimensions(newDimensions);
         if (left != result) {
-            RList newDimNames = left.getDimNames(attrLeftProfiles);
+            RList newDimNames = getDimNamesNode.getDimNames(left);
             if (hasDimNames.profile(newDimNames != null)) {
-                result.setDimNames(newDimNames);
+                setDimNamesNode.setDimNames(result, newDimNames);
             }
         }
         return result;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
index 7b6897b18b8ecdd0cc42a7c8aff3085a9b3c0a1e..d1422eebc32fb87cfcdb041b1b411aae9a1a2c35 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyOfRegAttributesNode.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.nodes.attributes;
 
-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.object.Property;
@@ -30,7 +29,6 @@ import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@@ -112,20 +110,14 @@ public abstract class CopyOfRegAttributesNode extends RBaseNode {
     protected void copyGeneric(RAbstractVector source, RVector<?> target) {
         DynamicObject orgAttributes = source.getAttributes();
         if (orgAttributes != null) {
-            Object newRowNames = null;
-
             Shape shape = orgAttributes.getShape();
             for (Property p : shape.getProperties()) {
                 String name = (String) p.getKey();
                 if (name != RRuntime.DIM_ATTR_KEY && name != RRuntime.DIMNAMES_ATTR_KEY && name != RRuntime.NAMES_ATTR_KEY) {
                     Object val = p.get(orgAttributes, shape);
                     target.initAttributes().define(name, val);
-                    if (name == RRuntime.ROWNAMES_ATTR_KEY) {
-                        newRowNames = val;
-                    }
                 }
             }
-            target.setInternalRowNames(newRowNames == null ? RNull.instance : newRowNames);
         }
     }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributeNode.java
index 0a28d002af32b82684ecf48e4f815256e363c859..858530667ff7dc7246205f084dd710e3e0b2d631 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetAttributeNode.java
@@ -22,16 +22,29 @@
  */
 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.object.Location;
 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;
 
+/**
+ * This node is responsible for retrieving a value from an arbitrary attribute. It accepts both
+ * {@link DynamicObject} and {@link RAttributable} instances as the first argument. If the first
+ * argument is {@link RAttributable} and its attributes are initialized, the recursive instance of
+ * this class is used to get the attribute value from the attributes.
+ */
 public abstract class GetAttributeNode extends AttributeAccessNode {
 
+    @Child private GetAttributeNode recursive;
+
     protected GetAttributeNode() {
     }
 
@@ -39,7 +52,7 @@ public abstract class GetAttributeNode extends AttributeAccessNode {
         return GetAttributeNodeGen.create();
     }
 
-    public abstract Object execute(DynamicObject attrs, String name);
+    public abstract Object execute(Object attrs, String name);
 
     @Specialization(limit = "3", //
                     guards = {"cachedName.equals(name)", "shapeCheck(shape, attrs)"}, //
@@ -58,4 +71,30 @@ public abstract class GetAttributeNode extends AttributeAccessNode {
         return attrs.get(name);
     }
 
+    @Specialization
+    protected Object getAttrFromAttributable(RAttributable x, String name,
+                    @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 null;
+        }
+
+        if (recursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursive = insert(create());
+        }
+
+        return recursive.execute(attributes, name);
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetFixedAttributeNode.java
index 35b79c0d788b2e5f0ac8b45900b880778437d283..5b1b1963faebb517b9f6156dad202f17cfa632b8 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetFixedAttributeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/GetFixedAttributeNode.java
@@ -22,38 +22,57 @@
  */
 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.object.Location;
 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.r.runtime.RRuntime;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetClassAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 
+/**
+ * This node is responsible for retrieving a value from the predefined (fixed) attribute. It accepts
+ * both {@link DynamicObject} and {@link RAttributable} instances as the first argument. If the
+ * first argument is {@link RAttributable} and its attributes are initialized, the recursive
+ * instance of this class is used to get the attribute value from the attributes.
+ */
 public abstract class GetFixedAttributeNode extends FixedAttributeAccessNode {
 
+    @Child private GetFixedAttributeNode recursive;
+
     protected GetFixedAttributeNode(String name) {
         super(name);
     }
 
     public static GetFixedAttributeNode create(String name) {
-        return GetFixedAttributeNodeGen.create(name);
+        if (SpecialAttributesFunctions.IsSpecialAttributeNode.isSpecialAttribute(name)) {
+            return SpecialAttributesFunctions.createGetSpecialAttributeNode(name);
+        } else {
+            return GetFixedAttributeNodeGen.create(name);
+        }
     }
 
     public static GetFixedAttributeNode createNames() {
-        return GetFixedAttributeNode.create(RRuntime.NAMES_ATTR_KEY);
+        return GetNamesAttributeNode.create();
     }
 
-    public static GetFixedAttributeNode createDim() {
-        return GetFixedAttributeNode.create(RRuntime.DIM_ATTR_KEY);
+    public static GetDimAttributeNode createDim() {
+        return GetDimAttributeNode.create();
     }
 
-    public static GetFixedAttributeNode createClass() {
-        return GetFixedAttributeNode.create(RRuntime.CLASS_ATTR_KEY);
+    public static GetClassAttributeNode createClass() {
+        return GetClassAttributeNode.create();
     }
 
-    public abstract Object execute(DynamicObject attrs);
+    public abstract Object execute(Object attr);
 
     protected boolean hasProperty(Shape shape) {
         return shape.hasProperty(name);
@@ -74,4 +93,31 @@ public abstract class GetFixedAttributeNode extends FixedAttributeAccessNode {
     protected Object getAttrFallback(DynamicObject attrs) {
         return attrs.get(name);
     }
+
+    @Specialization
+    protected Object getAttrFromAttributable(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 null;
+        }
+
+        if (recursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursive = insert(create(name));
+        }
+
+        return recursive.execute(attributes);
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/HasFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/HasFixedAttributeNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..fdff7b2c0f7c3418a2847f03020d043e0473e74f
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/HasFixedAttributeNode.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+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.object.Location;
+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.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
+
+/**
+ * This node is responsible for determining the existence of the predefined (fixed) attribute. It
+ * accepts both {@link DynamicObject} and {@link RAttributable} instances as the first argument. If
+ * the first argument is {@link RAttributable} and its attributes are initialized, the recursive
+ * instance of this class is used to determine the existence from the attributes.
+ */
+public abstract class HasFixedAttributeNode extends FixedAttributeAccessNode {
+
+    @Child private HasFixedAttributeNode recursive;
+
+    protected HasFixedAttributeNode(String name) {
+        super(name);
+    }
+
+    public static HasFixedAttributeNode create(String name) {
+        return HasFixedAttributeNodeGen.create(name);
+    }
+
+    public static HasFixedAttributeNode createDim() {
+        return HasFixedAttributeNodeGen.create(RRuntime.DIM_ATTR_KEY);
+    }
+
+    public abstract boolean execute(Object attr);
+
+    protected boolean hasProperty(Shape shape) {
+        return shape.hasProperty(name);
+    }
+
+    @Specialization(limit = "3", //
+                    guards = {"shapeCheck(shape, attrs)"}, //
+                    assumptions = {"shape.getValidAssumption()"})
+    @SuppressWarnings("unused")
+    protected boolean hasAttrCached(DynamicObject attrs,
+                    @Cached("lookupShape(attrs)") Shape shape,
+                    @Cached("lookupLocation(shape, name)") Location location) {
+        return location != null;
+    }
+
+    @Specialization(contains = "hasAttrCached")
+    @TruffleBoundary
+    protected boolean hasAttrFallback(DynamicObject attrs) {
+        return attrs.containsKey(name);
+    }
+
+    @Specialization
+    protected boolean hasAttrFromAttributable(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 false;
+        }
+
+        if (recursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursive = insert(create(name));
+        }
+
+        return recursive.execute(attributes);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
index 5d41dae1dbc3861f24152719abd03b86707050e0..b9c737057e670e1ed1d3938bbedabf588c48c737 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/RemoveFixedAttributeNode.java
@@ -26,10 +26,17 @@ 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.object.Property;
+import com.oracle.truffle.api.object.Location;
 import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.api.profiles.BranchProfile;
-import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveClassAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveDimAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveDimNamesAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveNamesAttributeNode;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 
 public abstract class RemoveFixedAttributeNode extends FixedAttributeAccessNode {
 
@@ -38,42 +45,64 @@ public abstract class RemoveFixedAttributeNode extends FixedAttributeAccessNode
     }
 
     public static RemoveFixedAttributeNode create(String name) {
-        return RemoveFixedAttributeNodeGen.create(name);
+        if (SpecialAttributesFunctions.IsSpecialAttributeNode.isSpecialAttribute(name)) {
+            return SpecialAttributesFunctions.createRemoveSpecialAttributeNode(name);
+        } else {
+            return RemoveFixedAttributeNodeGen.create(name);
+        }
     }
 
     public static RemoveFixedAttributeNode createNames() {
-        return RemoveFixedAttributeNode.create(RRuntime.NAMES_ATTR_KEY);
+        return RemoveNamesAttributeNode.create();
     }
 
     public static RemoveFixedAttributeNode createDim() {
-        return RemoveFixedAttributeNode.create(RRuntime.DIM_ATTR_KEY);
+        return RemoveDimAttributeNode.create();
     }
 
     public static RemoveFixedAttributeNode createDimNames() {
-        return RemoveFixedAttributeNode.create(RRuntime.DIMNAMES_ATTR_KEY);
+        return RemoveDimNamesAttributeNode.create();
     }
 
     public static RemoveFixedAttributeNode createClass() {
-        return RemoveFixedAttributeNode.create(RRuntime.CLASS_ATTR_KEY);
+        return RemoveClassAttributeNode.create();
     }
 
-    public abstract void execute(DynamicObject attrs);
+    public abstract void execute(Object attrs);
 
     @Specialization(limit = "3", //
-                    guards = {"shapeCheck(shape, attrs)"}, //
+                    guards = {"shapeCheck(shape, attrs)", "location == null"}, //
                     assumptions = {"shape.getValidAssumption()"})
-    protected void removeAttrCached(DynamicObject attrs,
-                    @Cached("lookupShape(attrs)") Shape shape,
-                    @Cached("lookupProperty(shape, name)") Property property) {
-        if (property != null) {
-            Shape newShape = attrs.getShape().removeProperty(property);
-            attrs.setShapeAndResize(shape, newShape);
-        }
+    protected void removeNonExistantAttr(@SuppressWarnings("unused") DynamicObject attrs,
+                    @SuppressWarnings("unused") @Cached("lookupShape(attrs)") Shape shape,
+                    @SuppressWarnings("unused") @Cached("lookupLocation(shape, name)") Location location) {
+        // do nothing
     }
 
-    @Specialization(contains = "removeAttrCached")
+    @Specialization
     @TruffleBoundary
     protected void removeAttrFallback(DynamicObject attrs) {
         attrs.delete(this.name);
     }
+
+    @Specialization
+    protected void removeAttrFromAttributable(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;
+        }
+
+        removeAttrFallback(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 b669fa8b59fdef3af23f82a6455888e6c742d45e..aecd9e3c0fc95b1f2f6cdb3c2bb80e3c5ff862da 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
@@ -22,6 +22,7 @@
  */
 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;
@@ -30,10 +31,27 @@ import com.oracle.truffle.api.object.FinalLocationException;
 import com.oracle.truffle.api.object.IncompatibleLocationException;
 import com.oracle.truffle.api.object.Location;
 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.RInternalError;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 
+/**
+ * This node is responsible for setting a value to an arbitrary attribute. It accepts both
+ * {@link DynamicObject} and {@link RAttributable} instances as the first argument. If the first
+ * argument is {@link RAttributable} and the attribute is a special one (i.e. names, dims, dimnames,
+ * rownames), a corresponding node defined in the {@link SpecialAttributesFunctions} class is
+ * created and the processing is delegated to it. If the first argument is {@link RAttributable} and
+ * the attribute is not a special one, it is made sure that the attributes in the first argument are
+ * initialized. Then the recursive instance of this class is used to set the attribute value to the
+ * attributes.
+ */
 public abstract class SetAttributeNode extends AttributeAccessNode {
 
+    @Child SetAttributeNode recursive;
+
     protected SetAttributeNode() {
     }
 
@@ -41,7 +59,7 @@ public abstract class SetAttributeNode extends AttributeAccessNode {
         return SetAttributeNodeGen.create();
     }
 
-    public abstract void execute(DynamicObject attrs, String name, Object value);
+    public abstract void execute(Object attrs, String name, Object value);
 
     @Specialization(limit = "3", //
                     guards = {
@@ -100,6 +118,57 @@ public abstract class SetAttributeNode extends AttributeAccessNode {
         receiver.define(name, value);
     }
 
+    protected static SpecialAttributesFunctions.SetSpecialAttributeNode createSpecAttrNode(String name) {
+        return SpecialAttributesFunctions.createSetSpecialAttributeNode(name);
+    }
+
+    @Specialization(limit = "3", //
+                    guards = {
+                                    "isSpecialAttributeNode.execute(name)",
+                                    "cachedName.equals(name)"
+                    })
+    @SuppressWarnings("unused")
+    protected void setSpecAttrInAttributable(RAttributable x, String name, Object value,
+                    @Cached("create()") SpecialAttributesFunctions.IsSpecialAttributeNode isSpecialAttributeNode,
+                    @Cached("name") String cachedName,
+                    @Cached("createSpecAttrNode(cachedName)") SpecialAttributesFunctions.SetSpecialAttributeNode setSpecAttrNode) {
+        setSpecAttrNode.execute(x, value);
+    }
+
+    @Specialization(contains = "setSpecAttrInAttributable", //
+                    guards = "isSpecialAttributeNode.execute(name)")
+    @SuppressWarnings("unused")
+    protected void setSpecAttrInAttributable(RAttributable x, String name, Object value,
+                    @Cached("create()") SpecialAttributesFunctions.IsSpecialAttributeNode isSpecialAttributeNode,
+                    @Cached("create()") SpecialAttributesFunctions.GenericSpecialAttributeNode genericSpecialAttrNode) {
+        genericSpecialAttrNode.execute(x, name, value);
+    }
+
+    @Specialization
+    protected void setAttrInAttributable(RAttributable x, String name, Object value,
+                    @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();
+            attributes = x.initAttributes();
+        }
+
+        if (recursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursive = insert(create());
+        }
+
+        recursive.execute(attributes, name, value);
+    }
+
     /**
      * Try to find the given property in the shape. Also returns null when the value cannot be store
      * into the location.
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 3f6b8b6bebd444f5cc1fd567b60b71d6cda3b159..fd79ede63e6e57041d801f7eecbd7355ca1f50e6 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
@@ -22,6 +22,7 @@
  */
 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;
@@ -30,36 +31,58 @@ import com.oracle.truffle.api.object.FinalLocationException;
 import com.oracle.truffle.api.object.IncompatibleLocationException;
 import com.oracle.truffle.api.object.Location;
 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.attributes.SpecialAttributesFunctions.SetSpecialAttributeNode;
 import com.oracle.truffle.r.runtime.RInternalError;
-import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeStorage;
 
+/**
+ * This node is responsible for setting a value to the predefined (fixed) attribute. It accepts both
+ * {@link DynamicObject} and {@link RAttributable} instances as the first argument. If the first
+ * argument is {@link RAttributable} and the attribute is a special one (i.e. names, dims, dimnames,
+ * rownames), a corresponding node defined in the {@link SpecialAttributesFunctions} class is
+ * created and the processing is delegated to it. If the first argument is {@link RAttributable} and
+ * the attribute is not a special one, it is made sure that the attributes in the first argument are
+ * initialized. Then the recursive instance of this class is used to set the attribute value to the
+ * attributes.
+ */
 public abstract class SetFixedAttributeNode extends FixedAttributeAccessNode {
 
+    @Child private SetFixedAttributeNode recursive;
+    @Child private SetSpecialAttributeNode setSpecialAttrNode;
+
     protected SetFixedAttributeNode(String name) {
         super(name);
     }
 
     public static SetFixedAttributeNode create(String name) {
-        return SetFixedAttributeNodeGen.create(name);
+        if (SpecialAttributesFunctions.IsSpecialAttributeNode.isSpecialAttribute(name)) {
+            return SpecialAttributesFunctions.createSetSpecialAttributeNode(name);
+        } else {
+            return SetFixedAttributeNodeGen.create(name);
+        }
     }
 
     public static SetFixedAttributeNode createNames() {
-        return SetFixedAttributeNode.create(RRuntime.NAMES_ATTR_KEY);
+        return SpecialAttributesFunctions.SetNamesAttributeNode.create();
     }
 
     public static SetFixedAttributeNode createDim() {
-        return SetFixedAttributeNode.create(RRuntime.DIM_ATTR_KEY);
+        return SpecialAttributesFunctions.SetDimAttributeNode.create();
     }
 
     public static SetFixedAttributeNode createDimNames() {
-        return SetFixedAttributeNode.create(RRuntime.DIMNAMES_ATTR_KEY);
+        return SpecialAttributesFunctions.SetDimNamesAttributeNode.create();
     }
 
     public static SetFixedAttributeNode createClass() {
-        return SetFixedAttributeNode.create(RRuntime.CLASS_ATTR_KEY);
+        return SpecialAttributesFunctions.SetClassAttributeNode.create();
     }
 
-    public abstract void execute(DynamicObject attrs, Object value);
+    public abstract void execute(Object attr, Object value);
 
     @Specialization(limit = "3", //
                     guards = {"shapeCheck(shape, attrs)", "location != null"}, //
@@ -74,10 +97,54 @@ public abstract class SetFixedAttributeNode extends FixedAttributeAccessNode {
         }
     }
 
+    @Specialization(limit = "3", //
+                    guards = {"shapeCheck(oldShape, attrs)", "oldLocation == null"}, //
+                    assumptions = {"oldShape.getValidAssumption()", "newShape.getValidAssumption()"})
+    protected static void setNewAttrCached(DynamicObject attrs, Object value,
+                    @Cached("lookupShape(attrs)") Shape oldShape,
+                    @SuppressWarnings("unused") @Cached("lookupLocation(oldShape, name)") Location oldLocation,
+                    @Cached("defineProperty(oldShape, name, value)") Shape newShape,
+                    @Cached("lookupLocation(newShape, name)") Location newLocation) {
+        try {
+            newLocation.set(attrs, value, oldShape, newShape);
+        } catch (IncompatibleLocationException ex) {
+            RInternalError.reportError(ex);
+        }
+    }
+
+    protected static Shape defineProperty(Shape oldShape, Object name, Object value) {
+        return oldShape.defineProperty(name, value, 0);
+    }
+
     @Specialization(contains = "setAttrCached")
     @TruffleBoundary
     protected void setFallback(DynamicObject attrs, Object value) {
         attrs.define(name, value);
     }
 
+    @Specialization
+    protected void setAttrInAttributable(RAttributable x, Object value,
+                    @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();
+            attributes = x.initAttributes();
+        }
+
+        if (recursive == null) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            recursive = insert(create(name));
+        }
+
+        recursive.execute(attributes, 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
new file mode 100644
index 0000000000000000000000000000000000000000..e596ca4f5b734b8f433cb47a4b912ab49de67e0a
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java
@@ -0,0 +1,1045 @@
+/*
+ * 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.
+ */
+package com.oracle.truffle.r.nodes.attributes;
+
+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.object.DynamicObject;
+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.attributes.SpecialAttributesFunctionsFactory.GetDimAttributeNodeGen;
+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.RAttributable;
+import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
+import com.oracle.truffle.r.runtime.data.RAttributesLayout;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RIntVector;
+import com.oracle.truffle.r.runtime.data.RInteger;
+import com.oracle.truffle.r.runtime.data.RLanguage;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.data.RScalarVector;
+import com.oracle.truffle.r.runtime.data.RSequence;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.RVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * This class defines a number of nodes used to handle the special attributes, such as names, dims,
+ * dimnames and rownames.
+ */
+public final class SpecialAttributesFunctions {
+
+    /**
+     * A node used in guards, for example, to determine whether an attribute is a special one.
+     */
+    public static final class IsSpecialAttributeNode extends RBaseNode {
+
+        private final BranchProfile namesProfile = BranchProfile.create();
+        private final BranchProfile dimProfile = BranchProfile.create();
+        private final BranchProfile dimNamesProfile = BranchProfile.create();
+        private final BranchProfile rowNamesProfile = BranchProfile.create();
+        private final BranchProfile classProfile = BranchProfile.create();
+
+        public static IsSpecialAttributeNode create() {
+            return new IsSpecialAttributeNode();
+        }
+
+        /**
+         * The fast-path method.
+         */
+        public boolean execute(String name) {
+            assert name.intern() == name;
+            if (name == RRuntime.NAMES_ATTR_KEY) {
+                namesProfile.enter();
+                return true;
+            } else if (name == RRuntime.DIM_ATTR_KEY) {
+                dimProfile.enter();
+                return true;
+            } else if (name == RRuntime.DIMNAMES_ATTR_KEY) {
+                dimNamesProfile.enter();
+                return true;
+            } else if (name == RRuntime.ROWNAMES_ATTR_KEY) {
+                rowNamesProfile.enter();
+                return true;
+            } else if (name == RRuntime.CLASS_ATTR_KEY) {
+                classProfile.enter();
+                return false;
+            }
+            return false;
+        }
+
+        /**
+         * The slow-path method.
+         */
+        public static boolean isSpecialAttribute(String name) {
+            assert name.intern() == name;
+            return name == RRuntime.NAMES_ATTR_KEY ||
+                            name == RRuntime.DIM_ATTR_KEY ||
+                            name == RRuntime.DIMNAMES_ATTR_KEY ||
+                            name == RRuntime.ROWNAMES_ATTR_KEY ||
+                            name == RRuntime.CLASS_ATTR_KEY;
+
+        }
+    }
+
+    /**
+     * A node for setting a value to any special attribute.
+     */
+    public static final class GenericSpecialAttributeNode extends RBaseNode {
+
+        private final BranchProfile namesProfile = BranchProfile.create();
+        private final BranchProfile dimProfile = BranchProfile.create();
+        private final BranchProfile dimNamesProfile = BranchProfile.create();
+        private final BranchProfile rowNamesProfile = BranchProfile.create();
+
+        @Child private SetNamesAttributeNode namesAttrNode;
+        @Child private SetDimAttributeNode dimAttrNode;
+        @Child private SetDimNamesAttributeNode dimNamesAttrNode;
+        @Child private SetRowNamesAttributeNode rowNamesAttrNode;
+
+        public static GenericSpecialAttributeNode create() {
+            return new GenericSpecialAttributeNode();
+        }
+
+        public void execute(RAttributable x, String name, Object value) {
+            assert name.intern() == name;
+            if (name == RRuntime.NAMES_ATTR_KEY) {
+                namesProfile.enter();
+                if (namesAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    namesAttrNode = insert(SetNamesAttributeNode.create());
+                }
+                namesAttrNode.execute(x, value);
+            } else if (name == RRuntime.DIM_ATTR_KEY) {
+                dimProfile.enter();
+                if (dimAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    dimAttrNode = insert(SetDimAttributeNode.create());
+                }
+                dimAttrNode.execute(x, value);
+            } else if (name == RRuntime.DIMNAMES_ATTR_KEY) {
+                dimNamesProfile.enter();
+                if (dimNamesAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    dimNamesAttrNode = insert(SetDimNamesAttributeNode.create());
+                }
+                dimNamesAttrNode.execute(x, value);
+            } else if (name == RRuntime.ROWNAMES_ATTR_KEY) {
+                rowNamesProfile.enter();
+                if (rowNamesAttrNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    rowNamesAttrNode = insert(SetRowNamesAttributeNode.create());
+                }
+                rowNamesAttrNode.execute(x, value);
+            } else if (name == RRuntime.CLASS_ATTR_KEY) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                throw RInternalError.unimplemented("The \"class\" attribute should be set using a separate method");
+            } else {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                throw RInternalError.shouldNotReachHere();
+            }
+        }
+    }
+
+    /**
+     * A factory method for creating a node setting the given special attribute.
+     *
+     * @param name the special attribute name
+     * @return the node
+     */
+    public static SetSpecialAttributeNode createSetSpecialAttributeNode(String name) {
+        assert name.intern() == name;
+        if (name == RRuntime.NAMES_ATTR_KEY) {
+            return SetNamesAttributeNode.create();
+        } else if (name == RRuntime.DIM_ATTR_KEY) {
+            return SetDimAttributeNode.create();
+        } else if (name == RRuntime.DIMNAMES_ATTR_KEY) {
+            return SetDimNamesAttributeNode.create();
+        } else if (name == RRuntime.ROWNAMES_ATTR_KEY) {
+            return SetRowNamesAttributeNode.create();
+        } else if (name == RRuntime.CLASS_ATTR_KEY) {
+            return SetClassAttributeNode.create();
+        } else {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    /**
+     * A factory method for creating a node removing the given special attribute.
+     *
+     * @param name the special attribute name
+     * @return the node
+     */
+    public static RemoveSpecialAttributeNode createRemoveSpecialAttributeNode(String name) {
+        assert name.intern() == name;
+        if (name == RRuntime.NAMES_ATTR_KEY) {
+            return RemoveNamesAttributeNode.create();
+        } else if (name == RRuntime.DIM_ATTR_KEY) {
+            return RemoveDimAttributeNode.create();
+        } else if (name == RRuntime.DIMNAMES_ATTR_KEY) {
+            return RemoveDimNamesAttributeNode.create();
+        } else if (name == RRuntime.ROWNAMES_ATTR_KEY) {
+            return RemoveRowNamesAttributeNode.create();
+        } else if (name == RRuntime.CLASS_ATTR_KEY) {
+            return RemoveClassAttributeNode.create();
+        } else {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    /**
+     * A factory method for creating a node retrieving the given special attribute.
+     *
+     * @param name the special attribute name
+     * @return the node
+     */
+    public static GetFixedAttributeNode createGetSpecialAttributeNode(String name) {
+        assert name.intern() == name;
+        if (name == RRuntime.NAMES_ATTR_KEY) {
+            return GetNamesAttributeNode.create();
+        } else if (name == RRuntime.DIM_ATTR_KEY) {
+            return GetDimAttributeNode.create();
+        } else if (name == RRuntime.DIMNAMES_ATTR_KEY) {
+            return GetDimNamesAttributeNode.create();
+        } else if (name == RRuntime.ROWNAMES_ATTR_KEY) {
+            return GetRowNamesAttributeNode.create();
+        } else if (name == RRuntime.CLASS_ATTR_KEY) {
+            return GetClassAttributeNode.create();
+        } else {
+            throw RInternalError.shouldNotReachHere();
+        }
+    }
+
+    /**
+     * The base class for the nodes setting values to special attributes.
+     */
+    public abstract static class SetSpecialAttributeNode extends SetFixedAttributeNode {
+
+        protected SetSpecialAttributeNode(String name) {
+            super(name);
+        }
+
+        public abstract void execute(RAttributable x, Object attrValue);
+
+    }
+
+    /**
+     * The base class for the nodes removing values from special attributes.
+     */
+    public abstract static class RemoveSpecialAttributeNode extends RemoveFixedAttributeNode {
+
+        protected RemoveSpecialAttributeNode(String name) {
+            super(name);
+        }
+
+        public abstract void execute(RAttributable x);
+
+        @Specialization(insertBefore = "removeAttrFromAttributable")
+        protected void removeAttrFromVector(RVector<?> x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("create()") BranchProfile attrEmptyProfile) {
+            DynamicObject attributes = x.getAttributes();
+            if (attributes == null) {
+                attrNullProfile.enter();
+                return;
+            }
+
+            attributes.delete(name);
+
+            if (attributes.isEmpty()) {
+                attrEmptyProfile.enter();
+                x.initAttributes(null);
+            }
+        }
+
+    }
+
+    public abstract static class SetNamesAttributeNode extends SetSpecialAttributeNode {
+
+        private final ConditionProfile nullDimNamesProfile = ConditionProfile.createBinaryProfile();
+
+        protected SetNamesAttributeNode() {
+            super(RRuntime.NAMES_ATTR_KEY);
+        }
+
+        public static SetNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.SetNamesAttributeNodeGen.create();
+        }
+
+        public void setNames(RAbstractContainer x, RStringVector newNames) {
+            if (nullDimNamesProfile.profile(newNames == null)) {
+                execute(x, RNull.instance);
+            } else {
+                execute(x, newNames);
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void resetDimNames(RAbstractContainer x, @SuppressWarnings("unused") RNull rnull,
+                        @Cached("create()") RemoveNamesAttributeNode removeNamesAttrNode) {
+            removeNamesAttrNode.execute(x);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setNamesInVector(RVector<?> x, RStringVector newNames,
+                        @Cached("create()") BranchProfile namesTooLongProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile useDimNamesProfile,
+                        @Cached("create()") GetDimAttributeNode getDimNode,
+                        @Cached("create()") SetDimNamesAttributeNode setDimNamesNode,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            RVector<?> xProfiled = xTypeProfile.profile(x);
+            if (newNames.getLength() > xProfiled.getLength()) {
+                namesTooLongProfile.enter();
+                throw RError.error(this, RError.Message.ATTRIBUTE_VECTOR_SAME_LENGTH, RRuntime.NAMES_ATTR_KEY, newNames.getLength(), xProfiled.getLength());
+            }
+
+            int[] dimensions = getDimNode.getDimensions(x);
+            if (useDimNamesProfile.profile(dimensions != null && dimensions.length == 1)) {
+                // for one dimensional array, "names" is really "dimnames[[1]]" (see R
+                // documentation for "names" function)
+                RList newDimNames = RDataFactory.createList(new Object[]{newNames});
+                newDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
+                setDimNamesNode.setDimNames(xProfiled, newDimNames);
+            } else {
+                assert newNames != xProfiled;
+                DynamicObject attrs = xProfiled.getAttributes();
+                if (attrs == null) {
+                    attrNullProfile.enter();
+                    attrs = RAttributesLayout.createNames(newNames);
+                    xProfiled.initAttributes(attrs);
+                    return;
+                }
+
+                super.setAttrInAttributable(xProfiled, newNames, attrNullProfile, attrStorageProfile, xTypeProfile);
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setNamesInContainer(RAbstractContainer x, RStringVector newNames,
+                        @Cached("createClassProfile()") ValueProfile contClassProfile) {
+            RAbstractContainer xProfiled = contClassProfile.profile(x);
+            xProfiled.setNames(newNames);
+        }
+    }
+
+    public abstract static class RemoveNamesAttributeNode extends RemoveSpecialAttributeNode {
+
+        protected RemoveNamesAttributeNode() {
+            super(RRuntime.NAMES_ATTR_KEY);
+        }
+
+        @Override
+        @Specialization
+        protected void removeAttrFallback(DynamicObject attrs) {
+            super.removeAttrFallback(attrs);
+        }
+
+        public static RemoveNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.RemoveNamesAttributeNodeGen.create();
+        }
+    }
+
+    public abstract static class GetNamesAttributeNode extends GetFixedAttributeNode {
+
+        protected GetNamesAttributeNode() {
+            super(RRuntime.NAMES_ATTR_KEY);
+        }
+
+        public static GetNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.GetNamesAttributeNodeGen.create();
+        }
+
+        public final RStringVector getNames(Object x) {
+            return (RStringVector) execute(x);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorNames(RVector<?> x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("create()") BranchProfile namesNullProfile,
+                        @Cached("create()") BranchProfile dimNamesAvlProfile,
+                        @Cached("create()") GetDimNamesAttributeNode getDimNames) {
+            RStringVector names = (RStringVector) super.getAttrFromAttributable(x, attrNullProfile, attrStorageProfile, xTypeProfile);
+            if (names == null) {
+                namesNullProfile.enter();
+                RList dimNames = getDimNames.getDimNames(x);
+                if (dimNames != null && dimNames.getLength() == 1) {
+                    dimNamesAvlProfile.enter();
+                    return dimNames.getDataAt(0);
+                }
+                return null;
+            }
+
+            return names;
+
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorNames(RAbstractContainer x,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            return xTypeProfile.profile(x).getNames(attrProfiles);
+        }
+    }
+
+    public abstract static class SetDimAttributeNode extends SetSpecialAttributeNode {
+
+        private final ConditionProfile nullDimProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile naDimProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile negativeDimProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile dimNotMatchLengthProfile = ConditionProfile.createBinaryProfile();
+        private final ValueProfile contArgClassProfile = ValueProfile.createClassProfile();
+        private final ValueProfile dimArgClassProfile = ValueProfile.createClassProfile();
+        private final LoopConditionProfile verifyLoopProfile = LoopConditionProfile.createCountingProfile();
+
+        protected SetDimAttributeNode() {
+            super(RRuntime.DIM_ATTR_KEY);
+        }
+
+        public static SetDimAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.SetDimAttributeNodeGen.create();
+        }
+
+        public void setDimensions(RAbstractContainer x, int[] dims) {
+            if (nullDimProfile.profile(dims == null)) {
+                execute(x, RNull.instance);
+            } else {
+                execute(x, RDataFactory.createIntVector(dims, RDataFactory.COMPLETE_VECTOR));
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void resetDims(RAbstractContainer x, @SuppressWarnings("unused") RNull rnull,
+                        @Cached("create()") RemoveDimAttributeNode removeDimAttrNode,
+                        @Cached("create()") SetDimNamesAttributeNode setDimNamesNode) {
+            removeDimAttrNode.execute(x);
+            setDimNamesNode.setDimNames(x, null);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setOneDimInVector(RVector<?> x, int dim,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            RAbstractContainer xProfiled = contArgClassProfile.profile(x);
+
+            int[] dims = new int[]{dim};
+            verifyOneDimensions(xProfiled.getLength(), dim);
+
+            RIntVector dimVec = RDataFactory.createIntVector(dims, RDataFactory.COMPLETE_VECTOR);
+
+            DynamicObject attrs = xProfiled.getAttributes();
+            if (attrs == null) {
+                attrNullProfile.enter();
+                attrs = RAttributesLayout.createDim(dimVec);
+                xProfiled.initAttributes(attrs);
+                return;
+            }
+
+            super.setAttrInAttributable(x, dimVec, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setDimsInVector(RVector<?> x, RAbstractIntVector dims,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            RAbstractContainer xProfiled = contArgClassProfile.profile(x);
+            verifyDimensions(xProfiled.getLength(), dims);
+
+            DynamicObject attrs = xProfiled.getAttributes();
+            if (attrs == null) {
+                attrNullProfile.enter();
+                attrs = RAttributesLayout.createDim(dims);
+                xProfiled.initAttributes(attrs);
+                return;
+            }
+
+            super.setAttrInAttributable(x, dims, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setDimsInContainerFallback(RAbstractContainer x, RAbstractIntVector dims,
+                        @Cached("create()") SetDimAttributeNode setDimNode) {
+            int[] dimsArr = dims.materialize().getDataCopy();
+            setDimNode.setDimensions(x, dimsArr);
+        }
+
+        private void verifyOneDimensions(int vectorLength, int dim) {
+            int length = dim;
+            if (naDimProfile.profile(RRuntime.isNA(dim))) {
+                throw RError.error(this, RError.Message.DIMS_CONTAIN_NA);
+            } else if (negativeDimProfile.profile(dim < 0)) {
+                throw RError.error(this, RError.Message.DIMS_CONTAIN_NEGATIVE_VALUES);
+            }
+            if (dimNotMatchLengthProfile.profile(length != vectorLength && vectorLength > 0)) {
+                CompilerDirectives.transferToInterpreter();
+                throw RError.error(this, RError.Message.DIMS_DONT_MATCH_LENGTH, length, vectorLength);
+            }
+        }
+
+        public void verifyDimensions(int vectorLength, RAbstractIntVector dims) {
+            RAbstractIntVector dimsProfiled = dimArgClassProfile.profile(dims);
+            int dimLen = dims.getLength();
+            verifyLoopProfile.profileCounted(dimLen);
+            int length = 1;
+            for (int i = 0; i < dimLen; i++) {
+                int dim = dimsProfiled.getDataAt(i);
+                if (naDimProfile.profile(RRuntime.isNA(dim))) {
+                    CompilerDirectives.transferToInterpreter();
+                    throw RError.error(this, RError.Message.DIMS_CONTAIN_NA);
+                } else if (negativeDimProfile.profile(dim < 0)) {
+                    CompilerDirectives.transferToInterpreter();
+                    throw RError.error(this, RError.Message.DIMS_CONTAIN_NEGATIVE_VALUES);
+                }
+                length *= dim;
+            }
+            if (length != vectorLength && vectorLength > 0) {
+                CompilerDirectives.transferToInterpreter();
+                throw RError.error(this, RError.Message.DIMS_DONT_MATCH_LENGTH, length, vectorLength);
+            }
+        }
+
+    }
+
+    public abstract static class RemoveDimAttributeNode extends RemoveSpecialAttributeNode {
+
+        protected RemoveDimAttributeNode() {
+            super(RRuntime.DIM_ATTR_KEY);
+        }
+
+        public static RemoveDimAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.RemoveDimAttributeNodeGen.create();
+        }
+
+        @Override
+        @Specialization
+        protected void removeAttrFallback(DynamicObject attrs) {
+            super.removeAttrFallback(attrs);
+        }
+
+    }
+
+    public abstract static class GetDimAttributeNode extends GetFixedAttributeNode {
+
+        private final BranchProfile isLanguageProfile = BranchProfile.create();
+        private final BranchProfile isPairListProfile = BranchProfile.create();
+        private final ConditionProfile nullDimsProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile nonEmptyDimsProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile twoDimsOrMoreProfile = ConditionProfile.createBinaryProfile();
+        private final ConditionProfile isContainerProfile = ConditionProfile.createBinaryProfile();
+
+        protected GetDimAttributeNode() {
+            super(RRuntime.DIM_ATTR_KEY);
+        }
+
+        public static GetDimAttributeNode create() {
+            return GetDimAttributeNodeGen.create();
+        }
+
+        public final int[] getDimensions(Object x) {
+            // Let's handle the following two types directly so as to avoid wrapping and unwrapping
+            // RIntVector. The getContainerDims spec would be invoked otherwise.
+            if (x instanceof RLanguage) {
+                isLanguageProfile.enter();
+                return ((RLanguage) x).getDimensions();
+            }
+            if (x instanceof RPairList) {
+                isPairListProfile.enter();
+                return ((RPairList) x).getDimensions();
+            }
+            RIntVector dims = (RIntVector) execute(x);
+            return nullDimsProfile.profile(dims == null) ? null : dims.getInternalStore();
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getScalarVectorDims(@SuppressWarnings("unused") RScalarVector x) {
+            return null;
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getScalarVectorDims(@SuppressWarnings("unused") RSequence x) {
+            return null;
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorDims(RAbstractVector x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            return super.getAttrFromAttributable(x, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getContainerDims(RAbstractContainer x,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile nullResultProfile) {
+            int[] res = xTypeProfile.profile(x).getDimensions();
+            return nullResultProfile.profile(res == null) ? null : RDataFactory.createIntVector(res, true);
+        }
+
+        public int nrows(Object x) {
+            if (isContainerProfile.profile(x instanceof RAbstractContainer)) {
+                RAbstractContainer xa = (RAbstractContainer) x;
+                int[] dims = getDimensions(xa);
+                if (nonEmptyDimsProfile.profile(dims != null && dims.length > 0)) {
+                    return dims[0];
+                } else {
+                    return xa.getLength();
+                }
+            } else {
+                throw RError.error(RError.SHOW_CALLER2, RError.Message.OBJECT_NOT_MATRIX);
+            }
+        }
+
+        public int ncols(Object x) {
+            if (isContainerProfile.profile(x instanceof RAbstractContainer)) {
+                RAbstractContainer xa = (RAbstractContainer) x;
+                int[] dims = getDimensions(xa);
+                if (nonEmptyDimsProfile.profile(dims != null && dims.length > 0)) {
+                    if (twoDimsOrMoreProfile.profile(dims.length >= 2)) {
+                        return dims[1];
+                    } else {
+                        return 1;
+                    }
+                } else {
+                    return 1;
+                }
+            } else {
+                throw RError.error(RError.SHOW_CALLER2, RError.Message.OBJECT_NOT_MATRIX);
+            }
+        }
+
+    }
+
+    public abstract static class SetDimNamesAttributeNode extends SetSpecialAttributeNode {
+
+        private final ConditionProfile nullDimNamesProfile = ConditionProfile.createBinaryProfile();
+
+        protected SetDimNamesAttributeNode() {
+            super(RRuntime.DIMNAMES_ATTR_KEY);
+        }
+
+        public static SetDimNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.SetDimNamesAttributeNodeGen.create();
+        }
+
+        public void setDimNames(RAbstractContainer x, RList dimNames) {
+            if (nullDimNamesProfile.profile(dimNames == null)) {
+                execute(x, RNull.instance);
+            } else {
+                execute(x, dimNames);
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void resetDimNames(RAbstractContainer x, @SuppressWarnings("unused") RNull rnull,
+                        @Cached("create()") RemoveDimNamesAttributeNode removeDimNamesAttrNode) {
+            removeDimNamesAttrNode.execute(x);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setDimNamesInVector(RVector<?> x, RList newDimNames,
+                        @Cached("create()") GetDimAttributeNode getDimNode,
+                        @Cached("create()") BranchProfile nullDimsProfile,
+                        @Cached("create()") BranchProfile dimsLengthProfile,
+                        @Cached("createCountingProfile()") LoopConditionProfile loopProfile,
+                        @Cached("create()") BranchProfile invalidDimProfile,
+                        @Cached("create()") BranchProfile nullDimProfile,
+                        @Cached("create()") BranchProfile resizeDimsProfile,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            int[] dimensions = getDimNode.getDimensions(x);
+            if (dimensions == null) {
+                nullDimsProfile.enter();
+                throw RError.error(this, RError.Message.DIMNAMES_NONARRAY);
+            }
+            int newDimNamesLength = newDimNames.getLength();
+            if (newDimNamesLength > dimensions.length) {
+                dimsLengthProfile.enter();
+                throw RError.error(this, RError.Message.DIMNAMES_DONT_MATCH_DIMS, newDimNamesLength,
+                                dimensions.length);
+            }
+
+            loopProfile.profileCounted(newDimNamesLength);
+            for (int i = 0; loopProfile.inject(i < newDimNamesLength); i++) {
+                Object dimObject = newDimNames.getDataAt(i);
+
+                if ((dimObject instanceof String && dimensions[i] != 1) ||
+                                (dimObject instanceof RStringVector && !isValidDimLength((RStringVector) dimObject, dimensions[i]))) {
+                    invalidDimProfile.enter();
+                    throw RError.error(this, RError.Message.DIMNAMES_DONT_MATCH_EXTENT, i + 1);
+                }
+
+                if (dimObject == null || (dimObject instanceof RStringVector && ((RStringVector) dimObject).getLength() == 0)) {
+                    nullDimProfile.enter();
+                    newDimNames.updateDataAt(i, RNull.instance, null);
+                }
+            }
+
+            RList resDimNames = newDimNames;
+            if (newDimNamesLength < dimensions.length) {
+                resizeDimsProfile.enter();
+                // resize the array and fill the missing entries with NULL-s
+                resDimNames = (RList) resDimNames.copyResized(dimensions.length, true);
+                resDimNames.setAttributes(newDimNames);
+                for (int i = newDimNamesLength; i < dimensions.length; i++) {
+                    resDimNames.updateDataAt(i, RNull.instance, null);
+                }
+            }
+            resDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
+
+            if (x.getAttributes() == null) {
+                attrNullProfile.enter();
+                x.initAttributes(RAttributesLayout.createDimNames(resDimNames));
+                return;
+            }
+
+            super.setAttrInAttributable(x, resDimNames, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        private static boolean isValidDimLength(RStringVector x, int expectedDim) {
+            int len = x.getLength();
+            return len == 0 || len == expectedDim;
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setDimNamesInContainer(RAbstractContainer x, RList dimNames, @Cached("createClassProfile()") ValueProfile contClassProfile) {
+            RAbstractContainer xProfiled = contClassProfile.profile(x);
+            xProfiled.setDimNames(dimNames);
+        }
+
+    }
+
+    public abstract static class RemoveDimNamesAttributeNode extends RemoveSpecialAttributeNode {
+
+        protected RemoveDimNamesAttributeNode() {
+            super(RRuntime.DIMNAMES_ATTR_KEY);
+        }
+
+        @Override
+        @Specialization
+        protected void removeAttrFallback(DynamicObject attrs) {
+            super.removeAttrFallback(attrs);
+        }
+
+        public static RemoveDimNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.RemoveDimNamesAttributeNodeGen.create();
+        }
+    }
+
+    public abstract static class GetDimNamesAttributeNode extends GetFixedAttributeNode {
+
+        protected GetDimNamesAttributeNode() {
+            super(RRuntime.DIMNAMES_ATTR_KEY);
+        }
+
+        public static GetDimNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.GetDimNamesAttributeNodeGen.create();
+        }
+
+        public final RList getDimNames(Object x) {
+            return (RList) execute(x);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorDimNames(RVector<?> x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            return super.getAttrFromAttributable(x, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorDimNames(RAbstractContainer x,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            return xTypeProfile.profile(x).getDimNames(attrProfiles);
+        }
+
+    }
+
+    public abstract static class SetRowNamesAttributeNode extends SetSpecialAttributeNode {
+
+        private final ConditionProfile nullRowNamesProfile = ConditionProfile.createBinaryProfile();
+
+        protected SetRowNamesAttributeNode() {
+            super(RRuntime.ROWNAMES_ATTR_KEY);
+        }
+
+        public static SetRowNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.SetRowNamesAttributeNodeGen.create();
+        }
+
+        public void setRowNames(RAbstractContainer x, RAbstractVector rowNames) {
+            if (nullRowNamesProfile.profile(rowNames == null)) {
+                execute(x, RNull.instance);
+            } else {
+                execute(x, rowNames);
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void resetRowNames(RVector<?> x, @SuppressWarnings("unused") RNull rnull,
+                        @Cached("create()") RemoveRowNamesAttributeNode removeRowNamesAttrNode) {
+            removeRowNamesAttrNode.execute(x);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setRowNamesInVector(RVector<?> x, RAbstractVector newRowNames,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            if (x.getAttributes() == null) {
+                attrNullProfile.enter();
+                x.initAttributes(RAttributesLayout.createRowNames(newRowNames));
+                return;
+            }
+            setAttrInAttributable(x, newRowNames, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void setRowNamesInContainer(RAbstractContainer x, RAbstractVector rowNames, @Cached("createClassProfile()") ValueProfile contClassProfile) {
+            RAbstractContainer xProfiled = contClassProfile.profile(x);
+            xProfiled.setRowNames(rowNames);
+        }
+
+    }
+
+    public abstract static class RemoveRowNamesAttributeNode extends RemoveSpecialAttributeNode {
+
+        protected RemoveRowNamesAttributeNode() {
+            super(RRuntime.DIMNAMES_ATTR_KEY);
+        }
+
+        public static RemoveRowNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.RemoveRowNamesAttributeNodeGen.create();
+        }
+
+        @Override
+        @Specialization
+        protected void removeAttrFallback(DynamicObject attrs) {
+            super.removeAttrFallback(attrs);
+        }
+    }
+
+    public abstract static class GetRowNamesAttributeNode extends GetFixedAttributeNode {
+
+        protected GetRowNamesAttributeNode() {
+            super(RRuntime.ROWNAMES_ATTR_KEY);
+        }
+
+        public static GetRowNamesAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.GetRowNamesAttributeNodeGen.create();
+        }
+
+        public Object getRowNames(RAbstractContainer x) {
+            return execute(x);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getScalarVectorRowNames(@SuppressWarnings("unused") RScalarVector x) {
+            return RNull.instance;
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getScalarVectorRowNames(@SuppressWarnings("unused") RSequence x) {
+            return RNull.class;
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorRowNames(RAbstractVector x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile nullRowNamesProfile) {
+            Object res = super.getAttrFromAttributable(x, attrNullProfile, attrStorageProfile, xTypeProfile);
+            return nullRowNamesProfile.profile(res == null) ? RNull.instance : res;
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorRowNames(RAbstractContainer x,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            return xTypeProfile.profile(x).getRowNames(attrProfiles);
+        }
+
+    }
+
+    public abstract static class SetClassAttributeNode extends SetSpecialAttributeNode {
+
+        protected SetClassAttributeNode() {
+            super(RRuntime.CLASS_ATTR_KEY);
+        }
+
+        public static SetClassAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.SetClassAttributeNodeGen.create();
+        }
+
+        public void reset(RAttributable x) {
+            execute(x, RNull.instance);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected <T> void handleVectorNullClass(RVector<T> vector, @SuppressWarnings("unused") RNull classAttr,
+                        @Cached("createClass()") RemoveFixedAttributeNode removeClassAttrNode,
+                        @Cached("createBinaryProfile()") ConditionProfile initAttrProfile,
+                        @Cached("create()") BranchProfile nullAttrProfile,
+                        @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);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected <T> void handleVector(RVector<T> vector, RStringVector classAttr,
+                        @Cached("createClass()") RemoveFixedAttributeNode removeClassAttrNode,
+                        @Cached("createBinaryProfile()") ConditionProfile initAttrProfile,
+                        @Cached("create()") BranchProfile nullAttrProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile nullClassProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile notNullClassProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+
+            DynamicObject attrs = vector.getAttributes();
+            boolean initializeAttrs = initAttrProfile.profile(attrs == null && classAttr != null && classAttr.getLength() != 0);
+            if (initializeAttrs) {
+                nullAttrProfile.enter();
+                attrs = RAttributesLayout.createClass(classAttr);
+                vector.initAttributes(attrs);
+            }
+            if (nullClassProfile.profile(attrs != null && (classAttr == null || classAttr.getLength() == 0))) {
+                removeAttributeMapping(vector, attrs, removeClassAttrNode);
+            } else if (notNullClassProfile.profile(classAttr != null && classAttr.getLength() != 0)) {
+                for (int i = 0; i < classAttr.getLength(); i++) {
+                    String attr = classAttr.getDataAt(i);
+                    if (RRuntime.CLASS_FACTOR.equals(attr)) {
+                        // TODO: Isn't this redundant when the same operation is done after the
+                        // loop?
+                        if (!initializeAttrs) {
+                            super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile);
+                        }
+                        // setClassAttrNode.execute(attrs, classAttr);
+                        if (vector.getElementClass() != RInteger.class) {
+                            // N.B. there used to be conversion to integer under certain
+                            // circumstances.
+                            // However, it seems that it was dead/obsolete code so it was removed.
+                            // Notes: this can only happen if the class is set by hand to some
+                            // non-integral vector, i.e. attr(doubles, 'class') <- 'factor'. GnuR
+                            // also
+                            // does not update the 'class' attr with other, possibly
+                            // valid classes when it reaches this error.
+                            throw RError.error(RError.SHOW_CALLER2, RError.Message.ADDING_INVALID_CLASS, "factor");
+                        }
+                    }
+                }
+
+                if (!initializeAttrs) {
+                    super.setAttrInAttributable(vector, classAttr, nullAttrProfile, attrStorageProfile, xTypeProfile);
+                }
+            }
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void handleAttributable(RAttributable x, @SuppressWarnings("unused") RNull classAttr) {
+            x.setClassAttr(null);
+        }
+
+        @Specialization(insertBefore = "setAttrInAttributable")
+        protected void handleAttributable(RAttributable x, RStringVector classAttr) {
+            x.setClassAttr(classAttr);
+        }
+
+        private static void removeAttributeMapping(RAttributable x, DynamicObject attrs, RemoveFixedAttributeNode removeClassAttrNode) {
+            if (attrs != null) {
+                removeClassAttrNode.execute(attrs);
+                if (attrs.isEmpty()) {
+                    x.initAttributes(null);
+                }
+            }
+        }
+
+    }
+
+    public abstract static class RemoveClassAttributeNode extends RemoveSpecialAttributeNode {
+
+        protected RemoveClassAttributeNode() {
+            super(RRuntime.CLASS_ATTR_KEY);
+        }
+
+        public static RemoveClassAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.RemoveClassAttributeNodeGen.create();
+        }
+
+        @Override
+        @Specialization
+        protected void removeAttrFallback(DynamicObject attrs) {
+            super.removeAttrFallback(attrs);
+        }
+
+    }
+
+    public abstract static class GetClassAttributeNode extends GetFixedAttributeNode {
+
+        protected GetClassAttributeNode() {
+            super(RRuntime.CLASS_ATTR_KEY);
+        }
+
+        public static GetClassAttributeNode create() {
+            return SpecialAttributesFunctionsFactory.GetClassAttributeNodeGen.create();
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorClass(RVector<?> x,
+                        @Cached("create()") BranchProfile attrNullProfile,
+                        @Cached("createBinaryProfile()") ConditionProfile attrStorageProfile,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile) {
+            return super.getAttrFromAttributable(x, attrNullProfile, attrStorageProfile, xTypeProfile);
+        }
+
+        @Specialization(insertBefore = "getAttrFromAttributable")
+        protected Object getVectorClass(RAbstractContainer x,
+                        @Cached("createClassProfile()") ValueProfile xTypeProfile,
+                        @Cached("create()") RAttributeProfiles attrProfiles) {
+            return xTypeProfile.profile(x).getClassAttr(attrProfiles);
+        }
+
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UnaryCopyAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UnaryCopyAttributesNode.java
index 16ae928994e5501164d6c035d71ee336d91c4284..c31cd1da74a52ee0c2792e5dba579aafb2a7be56 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UnaryCopyAttributesNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/UnaryCopyAttributesNode.java
@@ -26,8 +26,10 @@ 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.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.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -47,7 +49,9 @@ public abstract class UnaryCopyAttributesNode extends RBaseNode {
 
     protected final boolean copyAllAttributes;
 
-    protected final RAttributeProfiles attrSourceProfiles = RAttributeProfiles.create();
+    @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+    @Child protected GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
+    @Child protected GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     protected UnaryCopyAttributesNode(boolean copyAllAttributes) {
         this.copyAllAttributes = copyAllAttributes;
@@ -59,13 +63,13 @@ public abstract class UnaryCopyAttributesNode extends RBaseNode {
 
     public abstract RAbstractVector execute(RAbstractVector target, RAbstractVector left);
 
-    protected boolean containsMetadata(RAbstractVector vector, RAttributeProfiles attrProfiles) {
-        return vector instanceof RVector && vector.hasDimensions() || (copyAllAttributes && vector.getAttributes() != null) || vector.getNames(attrProfiles) != null ||
-                        vector.getDimNames(attrProfiles) != null;
+    protected boolean containsMetadata(RAbstractVector vector) {
+        return vector instanceof RVector && hasDimNode.execute(vector) || (copyAllAttributes && vector.getAttributes() != null) || getNamesNode.getNames(vector) != null ||
+                        getDimNamesNode.getDimNames(vector) != null;
     }
 
     @SuppressWarnings("unused")
-    @Specialization(guards = "!containsMetadata(source, attrSourceProfiles)")
+    @Specialization(guards = "!containsMetadata(source)")
     protected RAbstractVector copyNoMetadata(RAbstractVector target, RAbstractVector source) {
         return target;
     }
@@ -76,7 +80,7 @@ public abstract class UnaryCopyAttributesNode extends RBaseNode {
         return target;
     }
 
-    @Specialization(guards = {"!copyAllAttributes || target != source", "containsMetadata(source, attrSourceProfiles)"})
+    @Specialization(guards = {"!copyAllAttributes || target != source", "containsMetadata(source)"})
     protected RAbstractVector copySameLength(RAbstractVector target, RAbstractVector source, //
                     @Cached("create()") CopyOfRegAttributesNode copyOfReg, //
                     @Cached("createDim()") RemoveFixedAttributeNode removeDim, //
@@ -87,40 +91,36 @@ public abstract class UnaryCopyAttributesNode extends RBaseNode {
                     @Cached("createDimNames()") SetFixedAttributeNode putDimNames, //
                     @Cached("createBinaryProfile()") ConditionProfile noDimensions, //
                     @Cached("createBinaryProfile()") ConditionProfile hasNamesSource, //
-                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames) {
+                    @Cached("createBinaryProfile()") ConditionProfile hasDimNames,
+                    @Cached("create()") GetDimAttributeNode getDimsNode) {
         RVector<?> result = target.materialize();
 
         if (copyAllAttributes) {
             copyOfReg.execute(source, result);
         }
 
-        int[] newDimensions = source.getDimensions();
+        int[] newDimensions = getDimsNode.getDimensions(source);
         if (noDimensions.profile(newDimensions == null)) {
             DynamicObject attributes = result.getAttributes();
             if (attributes != null) {
                 removeDim.execute(attributes);
                 removeDimNames.execute(attributes);
-                result.setInternalDimNames(null);
             }
-            result.setInternalDimensions(null);
 
-            RStringVector vecNames = source.getNames(attrSourceProfiles);
+            RStringVector vecNames = getNamesNode.getNames(source);
             if (hasNamesSource.profile(vecNames != null)) {
                 putNames.execute(initAttributes.execute(result), vecNames);
-                result.setInternalNames(vecNames);
                 return result;
             }
             return result;
         }
 
         putDim.execute(initAttributes.execute(result), RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
-        result.setInternalDimensions(newDimensions);
 
-        RList newDimNames = source.getDimNames(attrSourceProfiles);
+        RList newDimNames = getDimNamesNode.getDimNames(source);
         if (hasDimNames.profile(newDimNames != null)) {
             putDimNames.execute(result.getAttributes(), newDimNames);
             newDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
-            result.setInternalDimNames(newDimNames);
             return result;
         }
         return result;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
index d535ea92bc02fa1d2c8c9afeac907fcbb0c97534..7d7294095202e7f5ee95e789e947a12832a2e9c4 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RExternalBuiltinNode.java
@@ -38,7 +38,6 @@ import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNodeGen;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 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;
@@ -74,7 +73,6 @@ public abstract class RExternalBuiltinNode extends RBaseNode {
     @Child private CastToVectorNode castVector;
     @Children private final CastNode[] argumentCasts;
 
-    protected final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
     protected final BranchProfile errorProfile = BranchProfile.create();
 
     public RExternalBuiltinNode() {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
index 3b7dc03d2d930df8d37386cc0fd332e510823f56..8dc96975ebadf202edc586ede634c365eb3475df 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/GetS4DataSlot.java
@@ -19,6 +19,7 @@ import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.RemoveFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.unary.CastToVectorNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNode;
 import com.oracle.truffle.r.nodes.unary.TypeofNodeGen;
@@ -28,7 +29,6 @@ import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RS4Object;
 import com.oracle.truffle.r.runtime.data.RShareable;
-import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
 
 // transcribed from src/main/attrib.c
@@ -42,6 +42,7 @@ public abstract class GetS4DataSlot extends Node {
     @Child private GetFixedAttributeNode dotDataAttrAccess;
     @Child private GetFixedAttributeNode dotXDataAttrAccess;
     @Child private TypeofNode typeOf = TypeofNodeGen.create();
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     private final BranchProfile shareable = BranchProfile.create();
 
@@ -72,6 +73,12 @@ public abstract class GetS4DataSlot extends Node {
                 shareable.enter();
                 obj = (RAttributable) ((RShareable) obj).copy();
             }
+
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+
             if (s3Class != null) {
                 if (s3ClassAttrRemove == null) {
                     assert castToVector == null;
@@ -81,9 +88,9 @@ public abstract class GetS4DataSlot extends Node {
 
                 }
                 s3ClassAttrRemove.execute(obj.initAttributes());
-                obj = obj.setClassAttr((RStringVector) castToVector.execute(s3Class));
+                setClassAttrNode.execute(obj, castToVector.execute(s3Class));
             } else {
-                obj = obj.setClassAttr(null);
+                setClassAttrNode.reset(obj);
             }
             obj.unsetS4();
             if (type == RType.S4Object) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
index b02d46ea7312a68103d2a0488edad5a155c4bc29..025378e9745a42cfa016d1f36041b5e97e88b84b 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/NewObject.java
@@ -12,10 +12,12 @@
  */
 package com.oracle.truffle.r.nodes.objects;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
@@ -26,7 +28,6 @@ import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RStringVector;
 
 // transcribed from src/main/objects.c
 public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
@@ -36,6 +37,7 @@ public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
     @Child private AccessSlotNode accessSlotPrototypeName = AccessSlotNodeGen.create(true, null, null);
     @Child private DuplicateNode duplicate = DuplicateNodeGen.create(true);
     @Child private GetFixedAttributeNode pckgAttrAccess = GetFixedAttributeNode.create(RRuntime.PCKG_ATTR_KEY);
+    @Child private SetClassAttributeNode setClassAttrNode;
 
     @Child private CastNode castStringScalar;
     @Child private CastNode castLogicalScalar;
@@ -62,7 +64,13 @@ public abstract class NewObject extends RExternalBuiltinNode.Arg1 {
         RAttributable valueAttr = (RAttributable) value;
         if (valueAttr instanceof RS4Object ||
                         (e instanceof RAttributable && ((RAttributable) e).getAttributes() != null && pckgAttrAccess.execute(((RAttributable) e).getAttributes()) != null)) {
-            valueAttr = valueAttr.setClassAttr((RStringVector) e);
+
+            if (setClassAttrNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setClassAttrNode = insert(SetClassAttributeNode.create());
+            }
+
+            setClassAttrNode.execute(valueAttr, e);
             valueAttr.setS4();
         }
         return value;
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
index bb33f7c87e692865c85bb056adbd3b6cb32987b2..a720fcbc354a2185eb2dfda7551dedf61aa7cce1 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/BinaryMapNode.java
@@ -29,6 +29,8 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
 import com.oracle.truffle.r.nodes.attributes.CopyAttributesNode;
 import com.oracle.truffle.r.nodes.attributes.CopyAttributesNodeGen;
+import com.oracle.truffle.r.nodes.attributes.HasFixedAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.primitive.BinaryMapNodeFactory.VectorMapBinaryInternalNodeGen;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RError;
@@ -63,6 +65,10 @@ public final class BinaryMapNode extends RBaseNode {
     @Child private VectorMapBinaryInternalNode vectorNode;
     @Child private BinaryMapFunctionNode function;
     @Child private CopyAttributesNode copyAttributes;
+    @Child private GetDimAttributeNode getLeftDimNode = GetDimAttributeNode.create();
+    @Child private GetDimAttributeNode getRightDimNode = GetDimAttributeNode.create();
+    @Child private HasFixedAttributeNode hasLeftDimNode = HasFixedAttributeNode.createDim();
+    @Child private HasFixedAttributeNode hasRightDimNode = HasFixedAttributeNode.createDim();
 
     // profiles
     private final Class<? extends RAbstractVector> leftClass;
@@ -142,9 +148,9 @@ public final class BinaryMapNode extends RBaseNode {
         }
     }
 
-    private static boolean differentDimensions(RAbstractVector left, RAbstractVector right) {
-        int[] leftDimensions = left.getDimensions();
-        int[] rightDimensions = right.getDimensions();
+    private boolean differentDimensions(RAbstractVector left, RAbstractVector right) {
+        int[] leftDimensions = getLeftDimNode.getDimensions(left);
+        int[] rightDimensions = getRightDimNode.getDimensions(right);
         int leftLength = leftDimensions.length;
         int rightLength = rightDimensions.length;
         if (leftLength != rightLength) {
@@ -227,7 +233,7 @@ public final class BinaryMapNode extends RBaseNode {
     }
 
     private Object applyVectorized(RAbstractVector left, RAbstractVector leftCast, int leftLength, RAbstractVector right, RAbstractVector rightCast, int rightLength) {
-        if (mayContainMetadata && (dimensionsProfile.profile(left.hasDimensions() && right.hasDimensions()))) {
+        if (mayContainMetadata && (dimensionsProfile.profile(hasLeftDimNode.execute(left) && hasRightDimNode.execute(right)))) {
             if (differentDimensions(left, right)) {
                 CompilerDirectives.transferToInterpreter();
                 throw RError.error(this, RError.Message.NON_CONFORMABLE_ARRAYS);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
index bb49fe7f101598457022b1d5854d3b0901d8de16..e0b9d614cae4b1e9d6a0fff7d469b945ce70213c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/primitive/UnaryMapNode.java
@@ -22,12 +22,18 @@
  */
 package com.oracle.truffle.r.nodes.primitive;
 
+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.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.LoopConditionProfile;
+import com.oracle.truffle.r.nodes.attributes.HasFixedAttributeNode;
+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.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.primitive.UnaryMapNodeFactory.MapUnaryVectorInternalNodeGen;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -50,6 +56,9 @@ public final class UnaryMapNode extends RBaseNode {
 
     @Child private UnaryMapFunctionNode scalarNode;
     @Child private MapUnaryVectorInternalNode vectorNode;
+    @Child private GetDimAttributeNode getDimNode;
+    @Child private SetDimAttributeNode setDimNode;
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
 
     // profiles
     private final Class<? extends RAbstractVector> operandClass;
@@ -170,6 +179,19 @@ public final class UnaryMapNode extends RBaseNode {
         if (containsMetadata(operand) && operand != target) {
             hasAttributesProfile.enter();
             result = result.materialize();
+
+            if (getDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getDimNode = insert(GetDimAttributeNode.create());
+            }
+
+            if (setDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                setDimNode = insert(SetDimAttributeNode.create());
+            }
+
+            setDimNode.setDimensions(result, getDimNode.getDimensions(operand));
+
             copyAttributesInternal((RVector<?>) result, operand);
         }
         return result;
@@ -178,16 +200,18 @@ public final class UnaryMapNode extends RBaseNode {
     private final ConditionProfile hasDimensionsProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile hasNamesProfile = ConditionProfile.createBinaryProfile();
 
+    @Child private HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+    @Child private GetDimNamesAttributeNode getDimNamesNode = GetDimNamesAttributeNode.create();
+
     private boolean containsMetadata(RAbstractVector vector) {
         return vector instanceof RVector &&
-                        (hasDimensionsProfile.profile(vector.hasDimensions()) || vector.getAttributes() != null || hasNamesProfile.profile(vector.getNames(attrProfiles) != null) ||
-                                        vector.getDimNames(attrProfiles) != null);
+                        (hasDimensionsProfile.profile(hasDimNode.execute(vector)) || vector.getAttributes() != null || hasNamesProfile.profile(getNamesNode.getNames(vector) != null) ||
+                                        getDimNamesNode.getDimNames(vector) != null);
     }
 
     @TruffleBoundary
     private void copyAttributesInternal(RVector<?> result, RAbstractVector attributeSource) {
         result.copyRegAttributesFrom(attributeSource);
-        result.setDimensions(attributeSource.getDimensions());
         result.copyNamesFrom(attrProfiles, attributeSource);
     }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
index e9b89627095e03cd06329d0eee7a0082a7ea2ab5..860e6461885dec3ed983f115cc5e3e2b81ca9abe 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastBaseNode.java
@@ -22,17 +22,21 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Fallback;
 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.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.attributes.SpecialAttributesFunctions.SetDimNamesAttributeNode;
 import com.oracle.truffle.r.runtime.NullProfile;
 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.RType;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RTypedValue;
@@ -46,7 +50,10 @@ public abstract class CastBaseNode extends CastNode {
     private final ConditionProfile hasDimNamesProfile = ConditionProfile.createBinaryProfile();
     private final NullProfile hasDimensionsProfile = NullProfile.create();
     private final NullProfile hasNamesProfile = NullProfile.create();
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create();
+    @Child private GetDimAttributeNode getDimNode;
+    @Child private SetDimNamesAttributeNode setDimNamesNode;
+    @Child private GetDimNamesAttributeNode getDimNamesNode;
 
     private final boolean preserveNames;
     private final boolean preserveDimensions;
@@ -56,13 +63,16 @@ public abstract class CastBaseNode extends CastNode {
         this.preserveNames = preserveNames;
         this.preserveDimensions = preserveDimensions;
         this.preserveAttributes = preserveAttributes;
+        if (preserveDimensions) {
+            getDimNamesNode = GetDimNamesAttributeNode.create();
+        }
     }
 
     public boolean preserveNames() {
         return preserveNames;
     }
 
-    public boolean preserveDimensions() {
+    public final boolean preserveDimensions() {
         return preserveDimensions;
     }
 
@@ -79,7 +89,11 @@ public abstract class CastBaseNode extends CastNode {
 
     protected int[] getPreservedDimensions(RAbstractContainer operand) {
         if (preserveDimensions()) {
-            return hasDimensionsProfile.profile(operand.getDimensions());
+            if (getDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getDimNode = insert(GetDimAttributeNode.create());
+            }
+            return hasDimensionsProfile.profile(getDimNode.getDimensions(operand));
         } else {
             return null;
         }
@@ -87,7 +101,7 @@ public abstract class CastBaseNode extends CastNode {
 
     protected RStringVector getPreservedNames(RAbstractContainer operand) {
         if (preserveNames()) {
-            return hasNamesProfile.profile(operand.getNames(attrProfiles));
+            return hasNamesProfile.profile(getNamesNode.getNames(operand));
         } else {
             return null;
         }
@@ -95,9 +109,13 @@ public abstract class CastBaseNode extends CastNode {
 
     protected void preserveDimensionNames(RAbstractContainer operand, RVector<?> ret) {
         if (preserveDimensions()) {
-            RList dimNames = operand.getDimNames(attrProfiles);
+            RList dimNames = getDimNamesNode.getDimNames(operand);
             if (hasDimNamesProfile.profile(dimNames != null)) {
-                ret.setDimNames((RList) dimNames.copy());
+                if (setDimNamesNode == null) {
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    setDimNamesNode = insert(SetDimNamesAttributeNode.create());
+                }
+                setDimNamesNode.setDimNames(ret, (RList) dimNames.copy());
             }
         }
     }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
index 60f816057c901fbe6863f0c7cc5460d7c54c4cf1..1178f3762ee7743577fe611918c14242c8f9592d 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastExpressionNode.java
@@ -22,10 +22,11 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNamesAttributeNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RType;
-import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RExpression;
 import com.oracle.truffle.r.runtime.data.RFunction;
@@ -36,8 +37,6 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 
 public abstract class CastExpressionNode extends CastBaseNode {
 
-    private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
-
     public abstract Object executeExpression(Object o);
 
     protected CastExpressionNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
@@ -85,7 +84,8 @@ public abstract class CastExpressionNode extends CastBaseNode {
     }
 
     @Specialization
-    protected RExpression doAbstractContainer(RAbstractContainer obj) {
+    protected RExpression doAbstractContainer(RAbstractContainer obj,
+                    @Cached("create()") GetNamesAttributeNode getNamesNode) {
         int len = obj.getLength();
         Object[] data = new Object[len];
         for (int i = 0; i < len; i++) {
@@ -94,7 +94,7 @@ public abstract class CastExpressionNode extends CastBaseNode {
         if (obj instanceof RList) {
             RList list = (RList) obj;
             // TODO other attributes
-            return RDataFactory.createExpression(data, list.getNames(attrProfiles));
+            return RDataFactory.createExpression(data, getNamesNode.getNames(list));
         } else {
             return RDataFactory.createExpression(data);
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
index 9d53e8623b97de1e410dd4ae8b0619e7fae4f224..14ea322f9f4f9e49447990349ea395b987d9f135 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/CastListNode.java
@@ -22,11 +22,14 @@
  */
 package com.oracle.truffle.r.nodes.unary;
 
+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.r.nodes.attributes.ArrayAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.context.RContext;
@@ -38,13 +41,14 @@ import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.data.RS4Object;
-import com.oracle.truffle.r.runtime.data.RStringVector;
 import com.oracle.truffle.r.runtime.data.RSymbol;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.env.REnvironment;
 
 public abstract class CastListNode extends CastBaseNode {
 
+    @Child private SetClassAttributeNode setClassAttrNode;
+
     public abstract RList executeList(Object o);
 
     protected CastListNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes) {
@@ -86,16 +90,24 @@ public abstract class CastListNode extends CastBaseNode {
     }
 
     @Specialization
-    protected RList doLanguage(RLanguage operand, @Cached("create()") ArrayAttributeNode attrAttrAccess) {
+    protected RList doLanguage(RLanguage operand,
+                    @Cached("create()") ArrayAttributeNode attrAttrAccess,
+                    @Cached("create()") SetAttributeNode setAttrNode) {
         RList result = RContext.getRRuntimeASTAccess().asList(operand);
         DynamicObject operandAttrs = operand.getAttributes();
         if (operandAttrs != null) {
             // result may already have names, so can't call RVector.copyAttributesFrom
             for (RAttributesLayout.RAttribute attr : attrAttrAccess.execute(operandAttrs)) {
                 if (attr.getName().equals(RRuntime.CLASS_ATTR_KEY)) {
-                    result.setClassAttr((RStringVector) attr.getValue());
+
+                    if (setClassAttrNode == null) {
+                        setClassAttrNode = insert(SetClassAttributeNode.create());
+                        CompilerDirectives.transferToInterpreterAndInvalidate();
+                    }
+
+                    setClassAttrNode.execute(result, attr.getValue());
                 } else {
-                    result.setAttr(attr.getName(), attr.getValue());
+                    setAttrNode.execute(result, attr.getName(), attr.getValue());
                 }
             }
         }
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 f46b860eaa0bb01f6161abce902c1a7bd32ae362..11fc0d9c9361611a2cf8e797fdf7282527963218 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
@@ -110,7 +110,7 @@ public abstract class UnaryNotNode extends RBuiltinNode {
             }
         }
         RLogicalVector resultVector = RDataFactory.createLogicalVector(result, na.neverSeenNA());
-        resultVector.copyAttributesFrom(attrProfiles, vector);
+        resultVector.copyAttributesFrom(vector);
         return resultVector;
     }
 
@@ -184,7 +184,7 @@ public abstract class UnaryNotNode extends RBuiltinNode {
             }
         }
         RRawVector resultVector = RDataFactory.createRawVector(result);
-        resultVector.copyAttributesFrom(attrProfiles, vector);
+        resultVector.copyAttributesFrom(vector);
         return resultVector;
     }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
index f5a84fb5c59a96e77b0457446cf562667c2ac246..13475477da4af40a519467e8e5d31948d45e7923 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RChannel.java
@@ -35,7 +35,6 @@ import com.oracle.truffle.r.runtime.context.RContext;
 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.RAttribute;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RLanguage;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
index ff6d84758b526a9a736b6bb7afe2e83c2be3fafd..88c9bc23ee170a940db7ad22673a263e0f771b9d 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSerialize.java
@@ -41,7 +41,6 @@ import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.RComplex;
 import com.oracle.truffle.r.runtime.data.RComplexVector;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
index a0f6d36b867504ad6a268ad5117452ca4f91f07a..b699372ccaae032d19a0777b141c5d47ba662ce7 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/conn/RConnection.java
@@ -30,7 +30,6 @@ import java.util.LinkedList;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.object.DynamicObject;
-import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.conn.ConnectionSupport.BaseRConnection;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
index 6d1ed9e4e3e8da595a2591307208691b93364e0a..2fcc8fee9eb0465109998dde9517ad8123ffda69 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RAttributable.java
@@ -142,7 +142,7 @@ public interface RAttributable extends RTypedValue {
      * initialized and will be just cleared, unless nullify is {@code true}.
      *
      * @param nullify Some implementations can force nullifying attributes instance if this flag is
-     *            set to {@code true}. Nullifying is not guaranteed for al implementations.
+     *            set to {@code true}. Nullifying is not guaranteed for all implementations.
      */
     default void resetAllAttributes(boolean nullify) {
         DynamicObject attributes = getAttributes();
@@ -164,6 +164,10 @@ public interface RAttributable extends RTypedValue {
         return (RStringVector) getAttr(profiles, RRuntime.CLASS_ATTR_KEY);
     }
 
+    default RStringVector getClassAttr() {
+        return (RStringVector) getAttr(RRuntime.CLASS_ATTR_KEY);
+    }
+
     /**
      * Returns {@code true} if and only if the value has a {@code class} attribute added explicitly.
      * When {@code true}, it is possible to call {@link RAttributable#getClassHierarchy()}.
@@ -172,6 +176,10 @@ public interface RAttributable extends RTypedValue {
         return getClassAttr(profiles) != null ? true : false;
     }
 
+    default boolean isObject() {
+        return getClassAttr() != null ? true : false;
+    }
+
     static void copyAttributes(RAttributable obj, DynamicObject attrs) {
         if (attrs == null) {
             return;
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 70c54ef9df0bf9ac8ab717578d672f8fc221402a..8f35974969087a286e79230a3b7af3788e483ff2 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
@@ -27,7 +27,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.CompilerDirectives.ValueType;
 import com.oracle.truffle.api.object.DynamicObject;
@@ -59,12 +58,13 @@ public final class RAttributesLayout {
     private static final AttrsLayout NAMES_ATTRS_LAYOUT = new AttrsLayout(RRuntime.NAMES_ATTR_KEY);
     private static final AttrsLayout DIM_ATTRS_LAYOUT = new AttrsLayout(RRuntime.DIM_ATTR_KEY);
     private static final AttrsLayout DIMNAMES_ATTRS_LAYOUT = new AttrsLayout(RRuntime.DIMNAMES_ATTR_KEY);
+    private static final AttrsLayout ROWNAMES_ATTRS_LAYOUT = new AttrsLayout(RRuntime.ROWNAMES_ATTR_KEY);
     private static final AttrsLayout NAMES_AND_DIM_ATTRS_LAYOUT = new AttrsLayout(RRuntime.NAMES_ATTR_KEY, RRuntime.DIM_ATTR_KEY);
     private static final AttrsLayout DIM_AND_DIMNAMES_ATTRS_LAYOUT = new AttrsLayout(RRuntime.DIM_ATTR_KEY, RRuntime.DIMNAMES_ATTR_KEY);
     private static final AttrsLayout CLASS_AND_CONNID_ATTRS_LAYOUT = new AttrsLayout(RRuntime.CLASS_ATTR_KEY, RRuntime.CONN_ID_ATTR_KEY);
 
-    public static final AttrsLayout[] LAYOUTS = {EMPTY_ATTRS_LAYOUT, CLASS_ATTRS_LAYOUT, NAMES_ATTRS_LAYOUT, DIM_ATTRS_LAYOUT, DIMNAMES_ATTRS_LAYOUT, NAMES_AND_DIM_ATTRS_LAYOUT,
-                    DIM_AND_DIMNAMES_ATTRS_LAYOUT};
+    public static final AttrsLayout[] LAYOUTS = {EMPTY_ATTRS_LAYOUT, CLASS_ATTRS_LAYOUT, NAMES_ATTRS_LAYOUT, DIM_ATTRS_LAYOUT, DIMNAMES_ATTRS_LAYOUT, ROWNAMES_ATTRS_LAYOUT,
+                    NAMES_AND_DIM_ATTRS_LAYOUT, DIM_AND_DIMNAMES_ATTRS_LAYOUT};
 
     private static final Map<String, ConstantShapesAndProperties> constantShapesAndLocationsForAttribute = new HashMap<>();
 
@@ -114,6 +114,13 @@ public final class RAttributesLayout {
                         new Property[]{
                                         CLASS_AND_CONNID_ATTRS_LAYOUT.properties[0]
                         }));
+        constantShapesAndLocationsForAttribute.put(RRuntime.ROWNAMES_ATTR_KEY, new ConstantShapesAndProperties(
+                        new Shape[]{
+                                        ROWNAMES_ATTRS_LAYOUT.shape
+                        },
+                        new Property[]{
+                                        ROWNAMES_ATTRS_LAYOUT.properties[0]
+                        }));
 
     }
 
@@ -147,6 +154,10 @@ public final class RAttributesLayout {
         return DIMNAMES_ATTRS_LAYOUT.factory.newInstance(dimNames);
     }
 
+    public static DynamicObject createRowNames(Object rowNames) {
+        return ROWNAMES_ATTRS_LAYOUT.factory.newInstance(rowNames);
+    }
+
     public static DynamicObject createNamesAndDim(Object names, Object dim) {
         return NAMES_AND_DIM_ATTRS_LAYOUT.factory.newInstance(names, dim);
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
index 94d1d71fb49c3ac82b4b3f74a69a2f5fa3e18356..65d16aef9b115d138c0defe6e90f03ab43125e2b 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RExpression.java
@@ -47,14 +47,14 @@ public class RExpression extends RListBase implements RAbstractVector {
 
     @Override
     protected RExpression internalCopy() {
-        return new RExpression(Arrays.copyOf(data, data.length), dimensions, null);
+        return new RExpression(Arrays.copyOf(data, data.length), getDimensions(), null);
     }
 
     @Override
     protected RExpression internalDeepCopy() {
         // TOOD: only used for nested list updates, but still could be made faster (through a
         // separate AST node?)
-        RExpression listCopy = new RExpression(Arrays.copyOf(data, data.length), dimensions, null);
+        RExpression listCopy = new RExpression(Arrays.copyOf(data, data.length), getDimensions(), null);
         for (int i = 0; i < listCopy.getLength(); i++) {
             Object el = listCopy.getDataAt(i);
             if (el instanceof RVector) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
index dd1d78c81978adf1ae7cd562a1042a5c9c0e90e1..cd52a76a925b0dc067d7554f1c8ea060c24942f1 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RLanguage.java
@@ -196,7 +196,7 @@ public class RLanguage extends RSharingAttributeStorage implements RAbstractCont
     }
 
     @Override
-    public void setNames(RStringVector newNames) {
+    public final void setNames(RStringVector newNames) {
         if (list == null) {
             /* See getNames */
             RContext.getRRuntimeASTAccess().setNames(this, newNames);
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
index dd97f6ea0d42c018c7089d82c5b38b1a1609dddd..8632d8f56dbb440bf7ed0b0722ea33c34bc0a5cd 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RList.java
@@ -44,14 +44,14 @@ public final class RList extends RListBase implements RAbstractListVector {
 
     @Override
     protected RList internalCopy() {
-        return new RList(Arrays.copyOf(data, data.length), dimensions, null);
+        return new RList(Arrays.copyOf(data, data.length), getDimensions(), null);
     }
 
     @Override
     protected RList internalDeepCopy() {
         // TOOD: only used for nested list updates, but still could be made faster (through a
         // separate AST node?)
-        RList listCopy = new RList(Arrays.copyOf(data, data.length), dimensions, null);
+        RList listCopy = new RList(Arrays.copyOf(data, data.length), getDimensions(), null);
         for (int i = 0; i < listCopy.getLength(); i++) {
             Object el = listCopy.getDataAt(i);
             if (el instanceof RVector) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
index 4ed316e9098910dadca1a832ced61ce207fdfee0..537d54d73051c4026054cb467d2661f4b271bf06 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RListBase.java
@@ -146,6 +146,7 @@ public abstract class RListBase extends RVector<Object[]> implements RAbstractLi
 
     @TruffleBoundary
     public final Object getNameAt(int index) {
+        RStringVector names = getNamesFromAttrs();
         if (names != null && names != null) {
             String name = names.getDataAt(index);
             if (name == RRuntime.STRING_NA) {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
index fabe9e5c925816dc58e278f0f2cd4aef95f55b2b..4b514edb83a7774ebb4e417fa35b4ab3373beb86 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
@@ -329,7 +329,7 @@ public class RPairList extends RSharingAttributeStorage implements RAbstractCont
     }
 
     @Override
-    public void setNames(RStringVector newNames) {
+    public final void setNames(RStringVector newNames) {
         Object p = this;
         for (int i = 0; i < newNames.getLength() && !isNull(p); i++) {
             RPairList pList = (RPairList) p;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
index e95a99e462d65a1da3124913f6ce212b761a123f..138c53d05712073d0e6941b8a566ef0c2db40e20 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RRaw.java
@@ -22,7 +22,6 @@
  */
 package com.oracle.truffle.r.runtime.data;
 
-import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives.ValueType;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.r.runtime.RType;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
index 4d9c43afdf1371f48b3e288e73d3c4bd153d17de..310bde8f45d7ac97bb6a82e9f6275a0cf11847a2 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RVector.java
@@ -33,7 +33,6 @@ import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RType;
 import com.oracle.truffle.r.runtime.SuppressFBWarnings;
-import com.oracle.truffle.r.runtime.data.RAttributesLayout.RAttribute;
 import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
 import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@@ -60,18 +59,10 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     private static final RStringVector implicitClassHeaderMatrix = RDataFactory.createStringVector(new String[]{RType.Matrix.getName()}, true);
 
     protected boolean complete; // "complete" means: does not contain NAs
-    protected int[] dimensions;
-    protected RStringVector names;
-    private RList dimNames;
-    // cache rownames for data frames as they are accessed at every data frame access
-    private Object rowNames;
 
     protected RVector(boolean complete, int length, int[] dimensions, RStringVector names) {
         this.complete = complete;
-        this.dimensions = dimensions;
         assert names != this;
-        this.names = names;
-        this.rowNames = RNull.instance;
         if (names != null) {
             // since this constructor is for internal use only, the assertion shouldn't fail
             assert names.getLength() == length : "size mismatch: " + names.getLength() + " vs. " + length;
@@ -85,7 +76,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
                     // one-dimensional arrays do not have names, only dimnames with one value
                     RList newDimNames = RDataFactory.createList(new Object[]{names});
                     initAttributes(RAttributesLayout.createDimAndDimNames(dimensionsVector, newDimNames));
-                    this.dimNames = newDimNames;
                 }
             }
         } else {
@@ -95,6 +85,39 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         }
     }
 
+    private int[] getDimensionsFromAttrs() {
+        if (attributes == null) {
+            return null;
+        } else {
+            RIntVector dims = (RIntVector) attributes.get(RRuntime.DIM_ATTR_KEY);
+            return dims == null ? null : dims.getInternalStore();
+        }
+    }
+
+    private RList getDimNamesFromAttrs() {
+        if (attributes == null) {
+            return null;
+        } else {
+            return (RList) attributes.get(RRuntime.DIMNAMES_ATTR_KEY);
+        }
+    }
+
+    private Object getRowNamesFromAttrs() {
+        if (attributes == null) {
+            return null;
+        } else {
+            return attributes.get(RRuntime.ROWNAMES_ATTR_KEY);
+        }
+    }
+
+    protected final RStringVector getNamesFromAttrs() {
+        if (attributes == null) {
+            return null;
+        } else {
+            return (RStringVector) attributes.get(RRuntime.NAMES_ATTR_KEY);
+        }
+    }
+
     /**
      * Intended for external calls where a mutable copy is needed.
      */
@@ -127,36 +150,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     }
 
     public final int[] getInternalDimensions() {
-        return dimensions;
-    }
-
-    public final void setInternalDimensions(int[] newDimensions) {
-        dimensions = newDimensions;
-    }
-
-    public final RStringVector getInternalNames() {
-        return names;
-    }
-
-    public final void setInternalNames(RStringVector newNames) {
-        assert newNames != this;
-        names = newNames;
-    }
-
-    public final RList getInternalDimNames() {
-        return dimNames;
-    }
-
-    public final void setInternalDimNames(RList newDimNames) {
-        dimNames = newDimNames;
-    }
-
-    public final Object getInternalRowNames() {
-        return rowNames;
-    }
-
-    public final void setInternalRowNames(Object newRowNames) {
-        rowNames = newRowNames;
+        return getDimensionsFromAttrs();
     }
 
     @Override
@@ -179,7 +173,9 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         if (attrProfiles.attrNullProfile(attributes == null)) {
             return null;
         } else {
+            RStringVector names = getNamesFromAttrs();
             if (attrProfiles.attrNullNamesProfile(names == null)) {
+                RList dimNames = getDimNames();
                 if (dimNames != null && dimNames.getLength() == 1) {
                     return (RStringVector) dimNames.getDataAt(0);
                 } else {
@@ -196,7 +192,9 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
      * performance-critical)
      */
     public final RStringVector getNames() {
+        RStringVector names = getNamesFromAttrs();
         if (names == null) {
+            RList dimNames = getDimNames();
             if (dimNames != null && dimNames.getLength() == 1) {
                 return (RStringVector) dimNames.getDataAt(0);
             } else {
@@ -212,6 +210,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         if (getNames(attrProfiles) == null) {
             return -1;
         }
+        RStringVector names = getNamesFromAttrs();
         for (int i = 0; i < names.getLength(); i++) {
             if (names.getDataAt(i).equals(name)) {
                 return i;
@@ -232,6 +231,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         }
         boolean oneMatch = false;
         int match = -1;
+        RStringVector names = getNamesFromAttrs();
         for (int i = 0; i < names.getLength(); i++) {
             if (names.getDataAt(i).startsWith(name)) {
                 if (oneMatch) {
@@ -339,7 +339,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
             putAttribute(RRuntime.NAMES_ATTR_KEY, newNames);
         }
         assert newNames != this;
-        this.names = newNames;
     }
 
     @Override
@@ -354,22 +353,20 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
             // whether it's one dimensional array or not, assigning null always removes the "names"
             // attribute
             removeAttributeMapping(RRuntime.NAMES_ATTR_KEY);
-            this.names = null;
         } else if (newNames != null) {
             if (newNames.getLength() > this.getLength()) {
                 throw RError.error(invokingNode, RError.Message.ATTRIBUTE_VECTOR_SAME_LENGTH, RRuntime.NAMES_ATTR_KEY, newNames.getLength(), this.getLength());
             }
-            if (this.dimensions != null && dimensions.length == 1) {
+            int[] dimensions = getDimensionsFromAttrs();
+            if (dimensions != null && dimensions.length == 1) {
                 // for one dimensional array, "names" is really "dimnames[[1]]" (see R documentation
                 // for "names" function)
                 RList newDimNames = RDataFactory.createList(new Object[]{newNames});
                 newDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
                 putAttribute(RRuntime.DIMNAMES_ATTR_KEY, newDimNames);
-                this.dimNames = newDimNames;
             } else {
                 putAttribute(RRuntime.NAMES_ATTR_KEY, newNames);
                 assert newNames != this;
-                this.names = newNames;
             }
         }
     }
@@ -380,7 +377,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     }
 
     public final RList getDimNames() {
-        return dimNames;
+        return getDimNamesFromAttrs();
     }
 
     /**
@@ -394,7 +391,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         } else {
             putAttribute(RRuntime.DIMNAMES_ATTR_KEY, newDimNames);
         }
-        this.dimNames = newDimNames;
     }
 
     @Override
@@ -407,8 +403,8 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     private void setDimNames(RList newDimNames, RBaseNode invokingNode) {
         if (attributes != null && newDimNames == null) {
             removeAttributeMapping(RRuntime.DIMNAMES_ATTR_KEY);
-            this.dimNames = null;
         } else if (newDimNames != null) {
+            int[] dimensions = getDimensionsFromAttrs();
             if (dimensions == null) {
                 throw RError.error(invokingNode, RError.Message.DIMNAMES_NONARRAY);
             }
@@ -445,7 +441,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
             }
             putAttribute(RRuntime.DIMNAMES_ATTR_KEY, resDimNames);
             resDimNames.elementNamePrefix = RRuntime.DIMNAMES_LIST_ELEMENT_NAME_PREFIX;
-            this.dimNames = resDimNames;
         }
     }
 
@@ -455,17 +450,16 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     }
 
     public final Object getRowNames() {
-        return rowNames;
+        Object rn = getRowNamesFromAttrs();
+        return rn == null ? RNull.instance : rn;
     }
 
     @Override
     public final void setRowNames(RAbstractVector newRowNames) {
         if (newRowNames == null) {
             removeAttributeMapping(RRuntime.ROWNAMES_ATTR_KEY);
-            this.rowNames = RNull.instance;
         } else {
             putAttribute(RRuntime.ROWNAMES_ATTR_KEY, newRowNames);
-            this.rowNames = newRowNames;
         }
     }
 
@@ -476,22 +470,36 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
 
     @Override
     public final boolean hasDimensions() {
-        return dimensions != null;
+        return attributes == null ? false : attributes.containsKey(RRuntime.DIM_ATTR_KEY);
+    }
+
+    public final boolean hasDimNames() {
+        return attributes == null ? false : attributes.containsKey(RRuntime.DIMNAMES_ATTR_KEY);
+    }
+
+    public final boolean hasRowNames() {
+        return attributes == null ? false : attributes.containsKey(RRuntime.ROWNAMES_ATTR_KEY);
+    }
+
+    public final boolean hasNames() {
+        return attributes == null ? false : attributes.containsKey(RRuntime.NAMES_ATTR_KEY);
     }
 
     @Override
     public final boolean isMatrix() {
+        int[] dimensions = getDimensionsFromAttrs();
         return dimensions != null && dimensions.length == 2;
     }
 
     @Override
     public final boolean isArray() {
+        int[] dimensions = getDimensionsFromAttrs();
         return dimensions != null && dimensions.length > 0;
     }
 
     @Override
     public final int[] getDimensions() {
-        return dimensions;
+        return getDimensionsFromAttrs();
     }
 
     /**
@@ -505,7 +513,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         } else {
             putAttribute(RRuntime.DIM_ATTR_KEY, RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
         }
-        this.dimensions = newDimensions;
     }
 
     @Override
@@ -521,7 +528,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
             verifyDimensions(getLength(), newDimensions, invokingNode);
             putAttribute(RRuntime.DIM_ATTR_KEY, RDataFactory.createIntVector(newDimensions, RDataFactory.COMPLETE_VECTOR));
         }
-        this.dimensions = newDimensions;
     }
 
     @Override
@@ -533,6 +539,10 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         return setClassAttrInternal(vector, classAttr);
     }
 
+    public abstract class CNode extends RBaseNode {
+
+    }
+
     private static RAbstractContainer setClassAttrInternal(RVector<?> vector, RStringVector classAttr) {
         if (vector.attributes == null && classAttr != null && classAttr.getLength() != 0) {
             vector.initAttributes();
@@ -562,10 +572,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     }
 
     public final void setAttributes(RVector<?> result) {
-        result.names = this.names;
-        result.dimNames = this.dimNames;
-        result.rowNames = this.rowNames;
-        result.dimensions = this.dimensions;
         if (this.attributes != null) {
             result.initAttributes(RAttributesLayout.copy(this.attributes));
         }
@@ -659,21 +665,13 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
 
     public abstract void transferElementSameType(int toIndex, RAbstractVector fromVector, int fromIndex);
 
-    public final RAttributable copyAttributesFrom(RAttributeProfiles attrProfiles, RAbstractContainer vector) {
+    public final RAttributable copyAttributesFrom(RAbstractContainer vector) {
         // it's meant to be used on a "fresh" vector with only dimensions potentially set
-        assert (this.names == null);
-        assert (this.dimNames == null);
-        assert (this.rowNames == RNull.instance);
-        assert (this.dimensions == null);
+        assert (!hasNames());
+        assert (!hasDimNames());
+        assert (!hasRowNames());
+        assert (!hasDimensions());
         assert (this.attributes == null || this.attributes.size() == 0) : this.attributes.size();
-        if (vector.getDimensions() == null || vector.getDimensions().length != 1) {
-            // only assign name attribute if it's not represented as dimnames (as is the case for
-            // one-dimensional array)
-            this.names = vector.getNames(attrProfiles);
-        }
-        this.dimNames = vector.getDimNames(attrProfiles);
-        this.rowNames = vector.getRowNames(attrProfiles);
-        this.dimensions = vector.getDimensions();
         DynamicObject vecAttributes = vector.getAttributes();
         if (vecAttributes != null) {
             initAttributes(RAttributesLayout.copy(vecAttributes));
@@ -687,13 +685,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
      * Internal version without profiles used in a rare (and already slow) case of double-to-int
      * vector conversion when setting class attribute
      */
-    protected final RAttributable copyAttributesFrom(RVector<?> vector) {
-        if (vector.getDimensions() == null || vector.getDimensions().length != 1) {
-            this.names = vector.getNames();
-        }
-        this.dimNames = vector.getDimNames();
-        this.rowNames = vector.getRowNames();
-        this.dimensions = vector.getDimensions();
+    protected final RAttributable copyAttributesFromVector(RVector<?> vector) {
         DynamicObject vecAttributes = vector.getAttributes();
         if (vecAttributes != null) {
             initAttributes(RAttributesLayout.copy(vecAttributes));
@@ -705,9 +697,9 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
 
     public final void copyNamesDimsDimNamesFrom(RAttributeProfiles attrProfiles, RAbstractVector vector, RBaseNode invokingNode) {
         // it's meant to be used on a "fresh" vector with only dimensions potentially set
-        assert (this.names == null);
-        assert (this.dimNames == null);
-        assert (this.dimensions == null);
+        assert (!hasDimNames());
+        assert (!hasDimNames());
+        assert (!hasDimensions());
         assert (this.attributes == null);
         // for some reason, names is copied first, then dims, then dimnames
         if (vector.getDimensions() == null || vector.getDimensions().length != 1) {
@@ -720,7 +712,8 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     }
 
     public final boolean copyNamesFrom(RAttributeProfiles attrProfiles, RAbstractVector vector) {
-        if (this.dimensions == null) {
+        int[] dimensions = getDimensionsFromAttrs();
+        if (dimensions == null) {
             RStringVector vecNames = vector.getNames(attrProfiles);
             if (vecNames != null) {
                 this.setNames(vecNames);
@@ -742,18 +735,13 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     public final RVector<ArrayT> copyRegAttributesFrom(RAbstractContainer vector) {
         DynamicObject orgAttributes = vector.getAttributes();
         if (orgAttributes != null) {
-            Object newRowNames = null;
             for (RAttributesLayout.RAttribute e : RAttributesLayout.asIterable(orgAttributes)) {
                 String name = e.getName();
                 if (name != RRuntime.DIM_ATTR_KEY && name != RRuntime.DIMNAMES_ATTR_KEY && name != RRuntime.NAMES_ATTR_KEY) {
                     Object val = e.getValue();
                     putAttribute(name, val);
-                    if (name == RRuntime.ROWNAMES_ATTR_KEY) {
-                        newRowNames = val;
-                    }
                 }
             }
-            this.rowNames = newRowNames == null ? RNull.instance : newRowNames;
         }
         return this;
     }
@@ -766,7 +754,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     private RVector<ArrayT> resize(int size, boolean resetAll) {
         this.complete &= getLength() >= size;
         RVector<ArrayT> res = this;
-        RStringVector oldNames = res.names;
+        RStringVector oldNames = res.getNamesFromAttrs();
         res = copyResized(size, true);
         if (this.isShared()) {
             assert res.isTemporary();
@@ -775,14 +763,13 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
         if (resetAll) {
             resetAllAttributes(oldNames == null);
         } else {
-            res.copyAttributesFrom(this);
+            res.copyAttributesFromVector(this);
             res.setDimensionsNoCheck(null);
             res.setDimNamesNoCheck(null);
         }
         if (oldNames != null) {
             oldNames = oldNames.resizeWithEmpty(size);
             res.putAttribute(RRuntime.NAMES_ATTR_KEY, oldNames);
-            res.names = oldNames;
         }
         return res;
     }
@@ -790,13 +777,10 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     @TruffleBoundary
     public final void resetDimensions(int[] newDimensions) {
         // reset all attributes other than dimensions;
-        this.dimensions = newDimensions;
         // whether we nullify dimensions or re-set them to a different value, names and dimNames
         // must be reset
-        this.names = null;
-        this.dimNames = null;
-        if (this.dimensions != null) {
-            putAttribute(RRuntime.DIM_ATTR_KEY, RDataFactory.createIntVector(this.dimensions, true));
+        if (newDimensions != null) {
+            putAttribute(RRuntime.DIM_ATTR_KEY, RDataFactory.createIntVector(newDimensions, true));
         } else {
             // nullifying dimensions does not reset regular attributes
             if (this.attributes != null) {
@@ -809,10 +793,6 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
 
     @Override
     public final void resetAllAttributes(boolean nullify) {
-        this.dimensions = null;
-        this.names = null;
-        this.dimNames = null;
-        this.rowNames = RNull.instance;
         if (nullify) {
             this.attributes = null;
         } else {
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 ca08ecfb6f6aab0c1e0ef5a998114127aaba55f3..8fef252e8753cf9ccc3ec41ebc0250fcd584ecfe 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
@@ -17948,6 +17948,10 @@ NULL
      [,1]
 [1,]   NA
 
+##com.oracle.truffle.r.test.builtins.TestBuiltin_dimassign.testdimassign12#
+#b <- c(a=1+2i,b=3+4i);dim(b) <- c(2,1);attributes(x)
+Error: object 'x' not found
+
 ##com.oracle.truffle.r.test.builtins.TestBuiltin_dimassign.testdimassign2#
 #argv <- list(structure(logical(0), .Dim = c(0L, 0L)), value = c(0L, 0L));`dim<-`(argv[[1]],argv[[2]]);
 <0 x 0 matrix>
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimassign.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimassign.java
index da6bda1e0cb7cc2ebe39516bfc246518da3e8a63..f76298ead2b14bf7a33e0e1fee0011b14932c8af 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimassign.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_dimassign.java
@@ -71,4 +71,10 @@ public class TestBuiltin_dimassign extends TestBase {
     public void testdimassign11() {
         assertEval("argv <- list(structure(NA, .Dim = c(1L, 1L)), value = c(1L, 1L));`dim<-`(argv[[1]],argv[[2]]);");
     }
+
+    @Test
+    public void testdimassign12() {
+        assertEval("b <- c(a=1+2i,b=3+4i);dim(b) <- c(2,1);attributes(x)");
+    }
+
 }