From 5c2d0033147495c9367b7379b2f9aee86288c851 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Mon, 12 Dec 2016 10:55:07 +0100
Subject: [PATCH] factor out String interning in Attr/UpdateAttr and make it
 thread safe

---
 .../truffle/r/nodes/builtin/base/Attr.java    | 35 ++----------
 .../r/nodes/builtin/base/UpdateAttr.java      | 55 +++++++++----------
 2 files changed, 33 insertions(+), 57 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
index 662b6e581b..0329bf95e8 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Attr.java
@@ -28,8 +28,6 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
@@ -42,11 +40,12 @@ import com.oracle.truffle.r.nodes.attributes.IterableAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateAttr.InternStringNode;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateAttrNodeGen.InternStringNodeGen;
 import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RError.Message;
 import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.RAttributable;
 import com.oracle.truffle.r.runtime.data.RAttributesLayout;
@@ -64,9 +63,9 @@ public abstract class Attr extends RBuiltinNode {
     private final ConditionProfile searchPartialProfile = ConditionProfile.createBinaryProfile();
     private final BranchProfile errorProfile = BranchProfile.create();
 
-    @CompilationFinal private String cachedName = "";
-    @CompilationFinal private String cachedInternedName = "";
     @Child private UpdateShareableChildValueNode sharedAttrUpdate = UpdateShareableChildValueNode.create();
+    @Child private InternStringNode intern = InternStringNodeGen.create();
+
     @Child private GetAttributeNode attrAccess = GetAttributeNode.create();
     @Child private IterableAttributeNode iterAttrAccess = IterableAttributeNode.create();
 
@@ -83,28 +82,6 @@ public abstract class Attr extends RBuiltinNode {
         casts.arg("exact").asLogicalVector().findFirst().map(toBoolean());
     }
 
-    private String intern(String name) {
-        if (cachedName == null) {
-            // unoptimized case
-            return Utils.intern(name);
-        }
-        if (cachedName == name) {
-            // cached case
-            return cachedInternedName;
-        }
-        CompilerDirectives.transferToInterpreterAndInvalidate();
-        // Checkstyle: stop StringLiteralEquality
-        if (cachedName == "") {
-            // Checkstyle: resume StringLiteralEquality
-            cachedName = name;
-            cachedInternedName = Utils.intern(name);
-        } else {
-            cachedName = null;
-            cachedInternedName = null;
-        }
-        return Utils.intern(name);
-    }
-
     private Object searchKeyPartial(DynamicObject attributes, String name) {
         Object val = RNull.instance;
 
@@ -141,7 +118,7 @@ public abstract class Attr extends RBuiltinNode {
 
     @Specialization(guards = "!isRowNamesAttr(name)")
     protected Object attr(RAbstractContainer container, String name, boolean exact) {
-        return attrRA(container, intern(name), exact);
+        return attrRA(container, intern.execute(name), exact);
     }
 
     @Specialization(guards = "isRowNamesAttr(name)")
@@ -163,7 +140,7 @@ public abstract class Attr extends RBuiltinNode {
     @TruffleBoundary
     protected Object attr(Object object, Object name, Object exact) {
         if (object instanceof RAttributable) {
-            return attrRA((RAttributable) object, intern((String) name), (boolean) exact);
+            return attrRA((RAttributable) object, intern.execute((String) name), (boolean) exact);
         } else {
             errorProfile.enter();
             throw RError.nyi(this, "object cannot be attributed");
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
index 34c1d680f8..44c90112ee 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateAttr.java
@@ -29,17 +29,21 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.PURE;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.dsl.TypeSystemReference;
+import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.attributes.SetAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetClassAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetDimAttributeNode;
 import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.SetRowNamesAttributeNode;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.base.UpdateAttrNodeGen.InternStringNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNode;
 import com.oracle.truffle.r.nodes.unary.CastIntegerNodeGen;
 import com.oracle.truffle.r.nodes.unary.CastListNode;
@@ -75,8 +79,25 @@ public abstract class UpdateAttr extends RBuiltinNode {
     @Child private SetAttributeNode setGenAttrNode;
     @Child private SetDimAttributeNode setDimNode;
 
-    @CompilationFinal private String cachedName = "";
-    @CompilationFinal private String cachedInternedName = "";
+    @Child private InternStringNode intern = InternStringNodeGen.create();
+
+    @TypeSystemReference(EmptyTypeSystemFlatLayout.class)
+    public abstract static class InternStringNode extends Node {
+
+        public abstract String execute(String value);
+
+        @Specialization(limit = "3", guards = "value == cachedValue")
+        protected static String internCached(@SuppressWarnings("unused") String value,
+                        @SuppressWarnings("unused") @Cached("value") String cachedValue,
+                        @Cached("intern(value)") String interned) {
+            return interned;
+        }
+
+        @Specialization(contains = "internCached")
+        protected static String intern(String value) {
+            return Utils.intern(value);
+        }
+    }
 
     @Override
     protected void createCasts(CastBuilder casts) {
@@ -118,31 +139,9 @@ public abstract class UpdateAttr extends RBuiltinNode {
         return (RAbstractVector) castVector.execute(value);
     }
 
-    private String intern(String name) {
-        if (cachedName == null) {
-            // unoptimized case
-            return Utils.intern(name);
-        }
-        if (cachedName == name) {
-            // cached case
-            return cachedInternedName;
-        }
-        CompilerDirectives.transferToInterpreterAndInvalidate();
-        // Checkstyle: stop StringLiteralEquality
-        if (cachedName == "") {
-            // Checkstyle: resume StringLiteralEquality
-            cachedName = name;
-            cachedInternedName = Utils.intern(name);
-        } else {
-            cachedName = null;
-            cachedInternedName = null;
-        }
-        return Utils.intern(name);
-    }
-
     @Specialization
     protected RAbstractContainer updateAttr(RAbstractContainer container, String name, RNull value) {
-        String internedName = intern(name);
+        String internedName = intern.execute(name);
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
         // the name is interned, so identity comparison is sufficient
         if (internedName == RRuntime.DIM_ATTR_KEY) {
@@ -187,7 +186,7 @@ public abstract class UpdateAttr extends RBuiltinNode {
 
     @Specialization(guards = "!nullValue(value)")
     protected RAbstractContainer updateAttr(RAbstractContainer container, String name, Object value) {
-        String internedName = intern(name);
+        String internedName = intern.execute(name);
         RAbstractContainer result = (RAbstractContainer) container.getNonShared();
         // the name is interned, so identity comparison is sufficient
         if (internedName == RRuntime.DIM_ATTR_KEY) {
@@ -246,7 +245,7 @@ public abstract class UpdateAttr extends RBuiltinNode {
         if (object instanceof RShareable) {
             object = ((RShareable) object).getNonShared();
         }
-        String internedName = intern((String) name);
+        String internedName = intern.execute((String) name);
         if (object instanceof RAttributable) {
             RAttributable attributable = (RAttributable) object;
             if (value == RNull.instance) {
-- 
GitLab