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 1cfa2b74dfa57ff0db9fdb43852431bc135a9491..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
@@ -19,6 +19,7 @@ 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;
@@ -48,9 +49,10 @@ public abstract class Cdist extends RExternalBuiltinNode.Arg4 {
     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,
-                    @Cached("create()") SetClassAttributeNode setClassAttrNode) {
-        int nr = RRuntime.nrows(x);
-        int nc = RRuntime.ncols(x);
+                    @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();
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..9517ffa7525c57410a9d4ff43420c87c7a132d08 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,7 +10,9 @@
  */
 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;
@@ -29,7 +31,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 +45,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..dd00fde2ef4e339fade9bf637eb08acc76c452d5 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,7 +10,9 @@
  */
 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;
@@ -25,9 +27,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.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..ed643cb629ef06cd93cc76696597613684206b0f 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,6 +23,7 @@ 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.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -61,10 +62,11 @@ 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) {
         checkErrorConditions(vector);
 
-        int[] dim = vector.getDimensions();
+        int[] dim = getDimsNode.getDimensions(vector);
         final int diml = dim.length;
 
         RVector<?> result = vector.createEmptySameType(vector.getLength(), vector.isComplete());
@@ -101,10 +103,11 @@ 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) {
         checkErrorConditions(vector);
 
-        int[] dim = vector.getDimensions();
+        int[] dim = getDimsNode.getDimensions(vector);
         int[] perm = getPermute(dim, permVector);
 
         int[] posV = new int[dim.length];
@@ -126,7 +129,8 @@ public abstract class APerm extends RBuiltinNode {
 
     @Specialization
     protected RAbstractVector aPerm(RAbstractVector vector, RAbstractStringVector permVector, byte resize,
-                    @Cached("create()") RAttributeProfiles dimNamesProfile) {
+                    @Cached("create()") RAttributeProfiles dimNamesProfile,
+                    @Cached("create()") GetDimAttributeNode getDimsNode) {
         RList dimNames = vector.getDimNames(dimNamesProfile);
         if (dimNames == null) {
             // TODO: this error is reported after IS_OF_WRONG_LENGTH in GnuR
@@ -146,7 +150,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);
     }
 
     private static int[] getReverse(int[] dim) {
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..7532f080c19d0b86026bed5a456fa030199eae76 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,7 @@ import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.RASTUtils;
+import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
@@ -95,6 +96,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;
 
@@ -122,6 +124,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();
@@ -275,7 +285,7 @@ public abstract class Bind extends RBaseNode {
             if (vecDimNames != null) {
                 firstDimNames = vecDimNames.getDataAt(dimInd);
             }
-        } else if (!vec.isArray() || vec.getDimensions().length == 1) {
+        } else if (!vec.isArray() || getVectorDimensions(vec).length == 1) {
             RStringVector names = vec.getNames(attrProfiles);
             firstDimNames = names == null ? RNull.instance : names;
         } else {
@@ -319,7 +329,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,7 +387,7 @@ public abstract class Bind extends RBaseNode {
     }
 
     protected int[] getDimensions(RAbstractVector vector) {
-        int[] dimensions = vector.getDimensions();
+        int[] dimensions = getVectorDimensions(vector);
         if (dimensions == null || dimensions.length != 2) {
             return type == BindType.cbind ? new int[]{vector.getLength(), 1} : new int[]{1, vector.getLength()};
         } else {
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..e59eb2809cc9c0ab9488e9fc92da42880af3a5b0 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,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.GetDimAttributeNode;
 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 +64,26 @@ 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];
+    protected RDoubleVector crossprod(RAbstractDoubleVector x, RAbstractDoubleVector y,
+                    @Cached("create()") GetDimAttributeNode getXDimsNode,
+                    @Cached("create()") GetDimAttributeNode getYDimsNode) {
+        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);
     }
 
-    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 +103,12 @@ 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) {
+        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), getResultDimsNode);
     }
 
     @Specialization
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/Drop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Drop.java
index 43cfb6a97d7bde5e346af117e8b0e2762f640d5d..e31f75656d5eb43a0ca4d37666f818a7de86e739 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,11 @@ 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.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
@@ -47,8 +49,8 @@ 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) {
+        int[] dims = getDimsNode.getDimensions(x);
         if (nullDimensions.profile(dims == null)) {
             return x;
         }
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..13dc1bcad62fb47e61c0beebf0f6e1a5cf7a9f2c 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
@@ -28,9 +28,11 @@ 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.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.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -74,12 +76,12 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     @Specialization
-    protected RLogicalVector isNA(RAbstractIntVector vector) {
+    protected RLogicalVector isNA(RAbstractIntVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             resultVector[i] = RRuntime.asLogical(RRuntime.isNA(vector.getDataAt(i)));
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
@@ -88,22 +90,22 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     @Specialization
-    protected RLogicalVector isNA(RAbstractDoubleVector vector) {
+    protected RLogicalVector isNA(RAbstractDoubleVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             resultVector[i] = RRuntime.asLogical(RRuntime.isNAorNaN(vector.getDataAt(i)));
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
-    protected RLogicalVector isNA(RComplexVector vector) {
+    protected RLogicalVector isNA(RComplexVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             RComplex complex = vector.getDataAt(i);
             resultVector[i] = RRuntime.asLogical(RRuntime.isNA(complex));
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
@@ -112,12 +114,12 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     @Specialization
-    protected RLogicalVector isNA(RStringVector vector) {
+    protected RLogicalVector isNA(RStringVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             resultVector[i] = RRuntime.asLogical(RRuntime.isNA(vector.getDataAt(i)));
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
@@ -152,12 +154,12 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     @Specialization
-    protected RLogicalVector isNA(RLogicalVector vector) {
+    protected RLogicalVector isNA(RLogicalVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             resultVector[i] = (RRuntime.isNA(vector.getDataAt(i)) ? RRuntime.LOGICAL_TRUE : RRuntime.LOGICAL_FALSE);
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
@@ -171,12 +173,12 @@ public abstract class IsNA extends RBuiltinNode {
     }
 
     @Specialization
-    protected RLogicalVector isNA(RRawVector vector) {
+    protected RLogicalVector isNA(RRawVector vector, @Cached("create()") GetDimAttributeNode getDimsNode) {
         byte[] resultVector = new byte[vector.getLength()];
         for (int i = 0; i < vector.getLength(); i++) {
             resultVector[i] = RRuntime.LOGICAL_FALSE;
         }
-        return createResult(resultVector, vector);
+        return createResult(resultVector, vector, getDimsNode);
     }
 
     @Specialization
@@ -193,8 +195,8 @@ public abstract class IsNA extends RBuiltinNode {
         return RRuntime.LOGICAL_FALSE;
     }
 
-    private RLogicalVector createResult(byte[] data, RAbstractVector originalVector) {
-        RLogicalVector result = RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR, originalVector.getDimensions(), originalVector.getNames(attrProfiles));
+    private RLogicalVector createResult(byte[] data, RAbstractVector originalVector, GetDimAttributeNode getDimsNode) {
+        RLogicalVector result = RDataFactory.createLogicalVector(data, RDataFactory.COMPLETE_VECTOR, getDimsNode.getDimensions(originalVector), originalVector.getNames(attrProfiles));
         RList dimNames = originalVector.getDimNames(attrProfiles);
         if (nullDimNamesProfile.profile(dimNames != null)) {
             result.setDimNames(dimNames);
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 45b9ae431e3b2dc55480c1af8cf867d546772e07..81003390bcf0aaede7fb48b504a4cb2146a7f844 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,10 +28,12 @@ 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.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
@@ -96,8 +98,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();
@@ -190,8 +192,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';
@@ -257,13 +259,13 @@ public class LaFunctions {
         }
 
         @Specialization
-        protected RList doQr(RAbstractDoubleVector aIn) {
+        protected RList doQr(RAbstractDoubleVector aIn, @Cached("create()") GetDimAttributeNode getDimsNode) {
             // 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];
@@ -312,7 +314,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();
 
@@ -321,8 +325,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();
@@ -387,9 +391,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;
@@ -475,9 +480,10 @@ 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) {
             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();
@@ -546,8 +552,10 @@ 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) {
+            int[] aDims = getADimsNode.getDimensions(a);
             int n = aDims[0];
             if (n == 0) {
                 throw RError.error(this, RError.Message.GENERIC, "'a' is 0-diml");
@@ -561,7 +569,7 @@ public class LaFunctions {
             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'");
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..d408b9a4d0e03fbc59dabe28e63bc21d273ff854 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,7 @@ 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.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.UnaryArithmeticBuiltinNode;
@@ -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()") RAttributeProfiles attrProfiles,
+                        @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, attrProfiles, getDimsNode);
         }
 
         @Specialization
         protected RDoubleVector log(RAbstractDoubleVector vector, double base,
                         @Cached("create()") CopyOfRegAttributesNode copyAttrsNode,
-                        @Cached("create()") RAttributeProfiles attrProfiles) {
+                        @Cached("create()") RAttributeProfiles attrProfiles,
+                        @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, attrProfiles, 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, RAttributeProfiles attrProfiles,
+                        GetDimAttributeNode getDimsNode) {
+            RDoubleVector result = RDataFactory.createDoubleVector(resultData, source.isComplete() && !RRuntime.isNA(base), getDimsNode.getDimensions(source), source.getNames(attrProfiles));
             copyAttrsNode.execute(source, result);
             return result;
         }
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..359655123762e0281c039706e26d6481b4346e28 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,6 +34,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.binary.BinaryMapArithmeticFunctionNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RError;
@@ -75,6 +76,9 @@ public abstract class MatMult extends RBuiltinNode {
     private final RAttributeProfiles bDimAttributeProfile = RAttributeProfiles.create();
     private final ConditionProfile noDimAttributes = ConditionProfile.createBinaryProfile();
 
+    @Child protected GetDimAttributeNode getADimsNode = GetDimAttributeNode.create();
+    @Child protected GetDimAttributeNode getBDimsNode = GetDimAttributeNode.create();
+
     protected abstract Object executeObject(Object a, Object b);
 
     private final NACheck na;
@@ -88,24 +92,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});
         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);
     }
 
@@ -268,12 +274,13 @@ public abstract class MatMult extends RBuiltinNode {
                     @Cached("createBinaryProfile()") ConditionProfile lengthEquals) {
         if (aIsMatrix.profile(a.isMatrix())) {
             if (bIsMatrix.profile(b.isMatrix())) {
-                int[] aDimensions = a.getDimensions();
-                int[] bDimensions = b.getDimensions();
+                int[] aDimensions = getADimsNode.getDimensions(a);
+                int[] bDimensions = getBDimsNode.getDimensions(b);
                 return doubleMatrixMultiply(a, b, aDimensions[0], aDimensions[1], bDimensions[0], bDimensions[1]);
             } 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())) {
@@ -287,8 +294,9 @@ public abstract class MatMult extends RBuiltinNode {
             }
         } 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())) {
@@ -328,14 +336,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 +363,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 +399,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 +457,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 +482,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 +515,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);
@@ -660,15 +676,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/NChar.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/NChar.java
index 1f0a0a0deb44b0cb65f736923ed39858c624ab8f..cb00025437bdadb08565c25e66bc32b2503a204a 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,6 +33,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.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -68,7 +69,8 @@ public abstract class NChar extends RBuiltinNode {
     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) {
         int len = vector.getLength();
         int[] result = new int[len];
         loopProfile.profileCounted(len);
@@ -80,7 +82,7 @@ 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));
+        RIntVector resultVector = RDataFactory.createIntVector(result, true, getDimNode.getDimensions(vector), vector.getNames(attrProfiles));
         RList dimNames = vector.getDimNames(attrProfiles);
         if (nullDimNamesProfile.profile(dimNames != null)) {
             resultVector.setDimNames(dimNames);
@@ -93,14 +95,15 @@ public abstract class NChar extends RBuiltinNode {
     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) {
         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));
+        RIntVector resultVector = RDataFactory.createIntVector(result, true, getDimNode.getDimensions(vector), vector.getNames(attrProfiles));
         RList dimNames = vector.getDimNames(attrProfiles);
         if (nullDimNamesProfile.profile(dimNames != null)) {
             resultVector.setDimNames(dimNames);
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..443a5505f5bc37089c8b11fb7562b3be52736cf9 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
@@ -30,9 +30,11 @@ import java.util.function.BiFunction;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
 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.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
@@ -55,6 +57,7 @@ public abstract class ToLowerOrUpper {
         private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
 
         @Child private CopyOfRegAttributesNode copyAttributes = CopyOfRegAttributesNodeGen.create();
+        @Child private GetDimAttributeNode getDimNode = GetDimAttributeNode.create();
 
         private StringMapNode() {
             // nothing to do
@@ -82,7 +85,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), vector.getNames(attrProfiles));
             copyAttributes.execute(vector, result);
             return result;
         }
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..ace3e951b770935861cfca0502a8f5fcaeac013b 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
@@ -32,6 +32,7 @@ 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.GetDimAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNode;
 import com.oracle.truffle.r.nodes.unary.CastStringNodeGen;
@@ -116,9 +117,10 @@ public abstract class UpdateDimNames extends RBuiltinNode {
 
     @Specialization(guards = "list.getLength() > 0")
     protected RAbstractContainer updateDimnames(RAbstractContainer container, RList list, //
-                    @Cached("createDimNames()") SetFixedAttributeNode attrSetter) {
+                    @Cached("createDimNames()") SetFixedAttributeNode attrSetter,
+                    @Cached("create()") GetDimAttributeNode getDimNode) {
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
-        setDimNames(result, convertToListOfStrings(list), attrSetter);
+        setDimNames(result, convertToListOfStrings(list), attrSetter, getDimNode);
         return result;
     }
 
@@ -128,11 +130,11 @@ public abstract class UpdateDimNames extends RBuiltinNode {
         throw RError.error(this, RError.Message.DIMNAMES_LIST);
     }
 
-    private void setDimNames(RAbstractContainer container, RList newDimNames, SetFixedAttributeNode attrSetter) {
+    private void setDimNames(RAbstractContainer container, RList newDimNames, SetFixedAttributeNode attrSetter, GetDimAttributeNode getDimNode) {
         assert newDimNames != null;
         if (isRVectorProfile.profile(container instanceof RVector)) {
             RVector<?> vector = (RVector<?>) container;
-            int[] dimensions = vector.getDimensions();
+            int[] dimensions = getDimNode.getDimensions(vector);
             if (dimensions == null) {
                 CompilerDirectives.transferToInterpreter();
                 throw RError.error(this, RError.Message.DIMNAMES_NONARRAY);
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/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/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/attributes/CopyAttributesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/CopyAttributesNode.java
index 31b4442825912ce16498bf016335ff54bb7eec2d..088dcd2711c487ae188cc1119e8d3236ec366253 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,6 +28,7 @@ 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.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -50,6 +51,8 @@ public abstract class CopyAttributesNode extends RBaseNode {
     protected final RAttributeProfiles attrLeftProfiles = RAttributeProfiles.create();
     protected final RAttributeProfiles attrRightProfiles = RAttributeProfiles.create();
 
+    @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+
     protected CopyAttributesNode(boolean copyAllAttributes) {
         this.copyAllAttributes = copyAllAttributes;
     }
@@ -61,7 +64,7 @@ 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 ||
+        return vector instanceof RVector && hasDimNode.execute(vector) || (copyAllAttributes && vector.getAttributes() != null) || vector.getDimNames(attrProfiles) != null ||
                         vector.getNames(attrProfiles) != null;
     }
 
@@ -124,7 +127,9 @@ 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) {
         if (LOG) {
             log("copyAttributes: ==");
             countEquals++;
@@ -139,9 +144,9 @@ 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();
@@ -206,7 +211,9 @@ 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) {
         if (LOG) {
             log("copyAttributes: <");
             countSmaller++;
@@ -217,10 +224,10 @@ 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();
@@ -260,7 +267,9 @@ 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) {
         if (LOG) {
             log("copyAttributes: >");
             countLarger++;
@@ -269,9 +278,9 @@ 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) {
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 24910e4e0a6b6326c8df97dd4f00a2813aa208cf..f4e6a5bd94306f9f3ebda9a665631a5a737c25cb 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
@@ -30,6 +30,7 @@ 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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 
@@ -55,15 +56,15 @@ public abstract class GetFixedAttributeNode extends FixedAttributeAccessNode {
         return GetFixedAttributeNode.create(RRuntime.NAMES_ATTR_KEY);
     }
 
-    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 abstract Object execute(Object attrs);
+    public abstract Object execute(Object attr);
 
     protected boolean hasProperty(Shape shape) {
         return shape.hasProperty(name);
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..fa44bfdcc6bb57d5fe39759889ac9fce390411f4
--- /dev/null
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/HasFixedAttributeNode.java
@@ -0,0 +1,98 @@
+/*
+ * 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.r.nodes.attributes.SpecialAttributesFunctions.GetDimAttributeNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+
+/**
+ * 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) {
+        DynamicObject attributes = 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/SetFixedAttributeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SetFixedAttributeNode.java
index b50ffbabe5201a036732c5f9b3e2c786c9ad7278..95f995a5481e993130e435e63cb367d8c5df0a15 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
@@ -94,6 +94,25 @@ 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) {
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java
index 3fe2f477a3a9bfa22e850272a8af3375d65ee3d4..1670540a7913dd1d94d54460cd34cc30230631cf 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/attributes/SpecialAttributesFunctions.java
@@ -29,10 +29,12 @@ import com.oracle.truffle.api.object.DynamicObject;
 import com.oracle.truffle.api.profiles.BranchProfile;
 import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.profiles.ValueProfile;
+import com.oracle.truffle.r.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.RIntVector;
 import com.oracle.truffle.r.runtime.data.RInteger;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
@@ -215,7 +217,8 @@ public final class SpecialAttributesFunctions {
         @Specialization
         protected void setOneDimInContainer(RAbstractContainer x, Integer dim, @Cached("createClassProfile()") ValueProfile contClassProfile) {
             RAbstractContainer xProfiled = contClassProfile.profile(x);
-            xProfiled.setDimensions(new int[]{dim});
+            // xProfiled.setDimensions(new int[]{dim});
+            xProfiled.setAttr(RRuntime.DIM_ATTR_KEY, new int[]{dim});
         }
 
         @Specialization
@@ -226,6 +229,60 @@ public final class SpecialAttributesFunctions {
 
     }
 
+    public abstract static class GetDimAttributeNode extends GetFixedAttributeNode {
+
+        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) {
+            RIntVector dims = (RIntVector) execute(x);
+            return nullDimsProfile.profile(dims == null) ? null : dims.getInternalStore();
+        }
+
+        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 {
 
         public static SetDimNamesAttributeNode create() {
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 fe2840463b46335b24eabe2075bab1007f50e33f..f695e4d9ee8677e1381cd9fed2a70f505e2fda3f 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
@@ -24,8 +24,10 @@ 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.nodes.Node.Child;
 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.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.data.RAttributeProfiles;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
@@ -49,6 +51,8 @@ public abstract class UnaryCopyAttributesNode extends RBaseNode {
 
     protected final RAttributeProfiles attrSourceProfiles = RAttributeProfiles.create();
 
+    @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+
     protected UnaryCopyAttributesNode(boolean copyAllAttributes) {
         this.copyAllAttributes = copyAllAttributes;
     }
@@ -60,7 +64,7 @@ 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 ||
+        return vector instanceof RVector && hasDimNode.execute(vector) || (copyAllAttributes && vector.getAttributes() != null) || vector.getNames(attrProfiles) != null ||
                         vector.getDimNames(attrProfiles) != null;
     }
 
@@ -87,14 +91,15 @@ 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) {
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..532ba2036fc54681ce27bf60de25cc590429e5cd 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
@@ -25,10 +25,13 @@ package com.oracle.truffle.r.nodes.primitive;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node.Child;
 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 +66,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 protected HasFixedAttributeNode hasLeftDimNode = HasFixedAttributeNode.createDim();
+    @Child protected HasFixedAttributeNode hasRightDimNode = HasFixedAttributeNode.createDim();
 
     // profiles
     private final Class<? extends RAbstractVector> leftClass;
@@ -142,9 +149,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 +234,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..df9aea109de8925484edd31bcfc2ecb3b21b9746 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,16 @@
  */
 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.nodes.Node.Child;
 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.primitive.UnaryMapNodeFactory.MapUnaryVectorInternalNodeGen;
 import com.oracle.truffle.r.nodes.profile.VectorLengthProfile;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -50,6 +54,7 @@ public final class UnaryMapNode extends RBaseNode {
 
     @Child private UnaryMapFunctionNode scalarNode;
     @Child private MapUnaryVectorInternalNode vectorNode;
+    @Child private GetDimAttributeNode getDimNode;
 
     // profiles
     private final Class<? extends RAbstractVector> operandClass;
@@ -170,7 +175,13 @@ public final class UnaryMapNode extends RBaseNode {
         if (containsMetadata(operand) && operand != target) {
             hasAttributesProfile.enter();
             result = result.materialize();
-            copyAttributesInternal((RVector<?>) result, operand);
+
+            if (getDimNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                getDimNode = insert(GetDimAttributeNode.create());
+            }
+
+            copyAttributesInternal((RVector<?>) result, operand, getDimNode.getDimensions(operand));
         }
         return result;
     }
@@ -178,16 +189,18 @@ public final class UnaryMapNode extends RBaseNode {
     private final ConditionProfile hasDimensionsProfile = ConditionProfile.createBinaryProfile();
     private final ConditionProfile hasNamesProfile = ConditionProfile.createBinaryProfile();
 
+    @Child protected HasFixedAttributeNode hasDimNode = HasFixedAttributeNode.createDim();
+
     private boolean containsMetadata(RAbstractVector vector) {
         return vector instanceof RVector &&
-                        (hasDimensionsProfile.profile(vector.hasDimensions()) || vector.getAttributes() != null || hasNamesProfile.profile(vector.getNames(attrProfiles) != null) ||
+                        (hasDimensionsProfile.profile(hasDimNode.execute(vector)) || vector.getAttributes() != null || hasNamesProfile.profile(vector.getNames(attrProfiles) != null) ||
                                         vector.getDimNames(attrProfiles) != null);
     }
 
     @TruffleBoundary
-    private void copyAttributesInternal(RVector<?> result, RAbstractVector attributeSource) {
+    private void copyAttributesInternal(RVector<?> result, RAbstractVector attributeSource, int[] dims) {
         result.copyRegAttributesFrom(attributeSource);
-        result.setDimensions(attributeSource.getDimensions());
+        result.setDimensions(dims);
         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..32b42ad68dcd797bd8f41902392963e6c28c7565 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,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.Fallback;
 import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.nodes.Node.Child;
 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.runtime.NullProfile;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
@@ -47,6 +50,7 @@ public abstract class CastBaseNode extends CastNode {
     private final NullProfile hasDimensionsProfile = NullProfile.create();
     private final NullProfile hasNamesProfile = NullProfile.create();
     private final RAttributeProfiles attrProfiles = RAttributeProfiles.create();
+    @Child private GetDimAttributeNode getDimNode;
 
     private final boolean preserveNames;
     private final boolean preserveDimensions;
@@ -79,7 +83,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;
         }
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 06c7279cc4862b2d689fc5b9a5f460de77367ad5..5d952237fb70f7e3aad9089653ce63df02ad65b3 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
@@ -28,9 +28,6 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.object.DynamicObject;
-import com.oracle.truffle.api.object.Location;
-import com.oracle.truffle.api.object.Property;
-import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
@@ -62,8 +59,6 @@ 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
-    private Shape attrShape;
-    private Location dimensionsLoc;
     protected RStringVector names;
     private RList dimNames;
     // cache rownames for data frames as they are accessed at every data frame access
@@ -96,34 +91,14 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
                 initAttributes(RAttributesLayout.createDim(RDataFactory.createIntVector(dimensions, true)));
             }
         }
-        updateLocationsFromAttrs();
-    }
-
-    private void updateLocationsFromAttrs() {
-        if (attributes == null) {
-            attrShape = null;
-            dimensionsLoc = null;
-        } else if (attributes.getShape() != attrShape) {
-            attrShape = attributes.getShape();
-            Property prop = attrShape.getProperty(RRuntime.DIM_ATTR_KEY);
-            if (prop == null) {
-                dimensionsLoc = null;
-            } else {
-                dimensionsLoc = prop.getLocation();
-            }
-            // TODO: the same for the other special attributes
-        }
     }
 
     private int[] getDimensionsFromAttrs() {
-        // Sync the shape
-        updateLocationsFromAttrs();
-
-        if (dimensionsLoc == null) {
+        if (attributes == null) {
             return null;
         } else {
-            RIntVector dims = (RIntVector) dimensionsLoc.get(attributes);
-            return dims.getInternalStore();
+            RIntVector dims = (RIntVector) attributes.get(RRuntime.DIM_ATTR_KEY);
+            return dims == null ? null : dims.getInternalStore();
         }
     }
 
@@ -507,9 +482,7 @@ public abstract class RVector<ArrayT> extends RSharingAttributeStorage implement
     @Override
     public final boolean hasDimensions() {
         // Sync the shape
-        updateLocationsFromAttrs();
-
-        return dimensionsLoc != null;
+        return attributes == null ? false : attributes.containsKey(RRuntime.DIM_ATTR_KEY);
     }
 
     @Override