From 583590f838125a531920524f07f3754b92a202d8 Mon Sep 17 00:00:00 2001
From: Lukas Stadler <lukas.stadler@oracle.com>
Date: Mon, 12 Dec 2016 12:13:16 +0100
Subject: [PATCH] thread safety in PrepareArguments (use DSL)

---
 .../nodes/function/call/PrepareArguments.java | 198 +++++++-----------
 1 file changed, 73 insertions(+), 125 deletions(-)

diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
index 60218f9968..5b48ec1019 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java
@@ -23,15 +23,23 @@
 package com.oracle.truffle.r.nodes.function.call;
 
 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.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.ExplodeLoop;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.function.ArgumentMatcher;
 import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation;
 import com.oracle.truffle.r.nodes.function.CallArgumentsNode;
 import com.oracle.truffle.r.nodes.function.FormalArguments;
 import com.oracle.truffle.r.nodes.function.RCallNode;
+import com.oracle.truffle.r.nodes.function.call.PrepareArgumentsFactory.PrepareArgumentsDefaultNodeGen;
+import com.oracle.truffle.r.nodes.function.call.PrepareArgumentsFactory.PrepareArgumentsExplicitNodeGen;
 import com.oracle.truffle.r.runtime.Arguments;
 import com.oracle.truffle.r.runtime.ArgumentsSignature;
 import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments;
@@ -44,171 +52,111 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
  * rules. It implements two different paths: one for arguments provided as an
  * {@link CallArgumentsNode}, i.e., unevaluated arguments, and another path for evaluated arguments.
  */
+@TypeSystemReference(EmptyTypeSystemFlatLayout.class)
 public abstract class PrepareArguments extends Node {
 
-    private static final int CACHE_SIZE = 4;
+    protected static final int CACHE_SIZE = 8;
 
-    /**
-     * Returns the argument values and corresponding signature. The signature represents the
-     * original call signature reordered in the same way as the arguments. For s3DefaultArguments
-     * motivation see {@link RCallNode#callGroupGeneric}.
-     */
-    public abstract RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, RCallNode call);
+    abstract static class PrepareArgumentsDefault extends PrepareArguments {
 
-    public static PrepareArguments create(RRootNode target, CallArgumentsNode args, boolean noOpt) {
-        return new UninitializedPrepareArguments(target, args, noOpt);
-    }
+        protected final RRootNode target;
+        protected final CallArgumentsNode sourceArguments; // not used as a node
+        protected final boolean noOpt;
 
-    public static PrepareArguments createExplicit(RRootNode target) {
-        return new UninitializedExplicitPrepareArguments(target);
-    }
+        static final class ArgumentsAndSignature extends Node {
+            @Children private final RNode[] matchedArguments;
+            private final ArgumentsSignature matchedSuppliedSignature;
 
-    @ExplodeLoop
-    private static RArgsValuesAndNames executeArgs(RNode[] arguments, ArgumentsSignature suppliedSignature, VirtualFrame frame) {
-        Object[] result = new Object[arguments.length];
-        for (int i = 0; i < arguments.length; i++) {
-            result[i] = arguments[i].execute(frame);
+            protected ArgumentsAndSignature(RNode[] matchedArguments, ArgumentsSignature matchedSuppliedSignature) {
+                this.matchedArguments = matchedArguments;
+                this.matchedSuppliedSignature = matchedSuppliedSignature;
+            }
         }
-        return new RArgsValuesAndNames(result, suppliedSignature);
-    }
-
-    private static RArgsValuesAndNames executeArgs(Arguments<RNode> matched, VirtualFrame frame) {
-        return executeArgs(matched.getArguments(), matched.getSignature(), frame);
-    }
-
-    private static final class UninitializedPrepareArguments extends PrepareArguments {
 
-        private final RRootNode target;
-        private final CallArgumentsNode sourceArguments; // not used as a node
-        private final boolean noOpt;
-        private int depth = CACHE_SIZE;
+        protected static ArgumentsSignature getSignatureOrNull(RArgsValuesAndNames args) {
+            return args == null ? null : args.getSignature();
+        }
 
-        UninitializedPrepareArguments(RRootNode target, CallArgumentsNode sourceArguments, boolean noOpt) {
+        protected PrepareArgumentsDefault(RRootNode target, CallArgumentsNode sourceArguments, boolean noOpt) {
             this.target = target;
             this.sourceArguments = sourceArguments;
             this.noOpt = noOpt;
         }
 
-        @Override
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            PrepareArguments next;
-            if (depth-- > 0) {
-                next = new CachedPrepareArguments(this, target, call, sourceArguments, varArgs == null ? null : varArgs.getSignature(), s3DefaultArguments, noOpt);
-            } else {
-                next = new GenericPrepareArguments(target, sourceArguments);
-            }
-            return replace(next).execute(frame, varArgs, s3DefaultArguments, call);
-        }
-    }
-
-    private static final class CachedPrepareArguments extends PrepareArguments {
-
-        @Child private PrepareArguments next;
-        @Children private final RNode[] matchedArguments;
-        private final ArgumentsSignature matchedSuppliedSignature;
-        private final ArgumentsSignature cachedVarArgSignature;
-        private final Object cachedS3DefaultArguments;
-
-        CachedPrepareArguments(PrepareArguments next, RRootNode target, RCallNode call, CallArgumentsNode args, ArgumentsSignature varArgSignature, S3DefaultArguments s3DefaultArguments,
-                        boolean noOpt) {
-            this.next = next;
-            cachedVarArgSignature = varArgSignature;
-            Arguments<RNode> matched = ArgumentMatcher.matchArguments(target, args, varArgSignature, s3DefaultArguments, call, noOpt);
-            this.matchedArguments = matched.getArguments();
-            this.matchedSuppliedSignature = matched.getSignature();
-            this.cachedS3DefaultArguments = s3DefaultArguments;
+        protected ArgumentsAndSignature createArguments(RCallNode call, ArgumentsSignature varArgSignature, S3DefaultArguments s3DefaultArguments) {
+            Arguments<RNode> matched = ArgumentMatcher.matchArguments(target, sourceArguments, varArgSignature, s3DefaultArguments, call, noOpt);
+            return new ArgumentsAndSignature(matched.getArguments(), matched.getSignature());
         }
 
-        @Override
         @ExplodeLoop
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
-            assert (cachedVarArgSignature != null) == (varArgs != null);
-            if ((cachedVarArgSignature == null || cachedVarArgSignature == varArgs.getSignature()) && cachedS3DefaultArguments == s3DefaultArguments) {
-                return executeArgs(matchedArguments, matchedSuppliedSignature, frame);
+        private static RArgsValuesAndNames executeArgs(RNode[] arguments, ArgumentsSignature suppliedSignature, VirtualFrame frame) {
+            Object[] result = new Object[arguments.length];
+            for (int i = 0; i < arguments.length; i++) {
+                result[i] = arguments[i].execute(frame);
             }
-            return next.execute(frame, varArgs, s3DefaultArguments, call);
+            return new RArgsValuesAndNames(result, suppliedSignature);
         }
-    }
-
-    private static final class GenericPrepareArguments extends PrepareArguments {
 
-        private final RRootNode target;
-        private final CallArgumentsNode args; // not used as a node
-
-        GenericPrepareArguments(RRootNode target, CallArgumentsNode args) {
-            this.target = target;
-            this.args = args;
+        @Specialization(limit = "CACHE_SIZE", guards = {"cachedVarArgSignature == null || cachedVarArgSignature == varArgs.getSignature()", "cachedS3DefaultArguments == s3DefaultArguments"})
+        public RArgsValuesAndNames prepare(VirtualFrame frame, RArgsValuesAndNames varArgs, @SuppressWarnings("unused") S3DefaultArguments s3DefaultArguments,
+                        @SuppressWarnings("unused") RCallNode call,
+                        @Cached("getSignatureOrNull(varArgs)") ArgumentsSignature cachedVarArgSignature,
+                        @Cached("createArguments(call, cachedVarArgSignature, s3DefaultArguments)") ArgumentsAndSignature arguments,
+                        @SuppressWarnings("unused") @Cached("s3DefaultArguments") S3DefaultArguments cachedS3DefaultArguments) {
+            assert (cachedVarArgSignature != null) == (varArgs != null);
+            return executeArgs(arguments.matchedArguments, arguments.matchedSuppliedSignature, frame);
         }
 
-        @Override
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
+        @Fallback
+        public RArgsValuesAndNames prepareGeneric(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, @SuppressWarnings("unused") RCallNode call) {
             CompilerDirectives.transferToInterpreter();
             ArgumentsSignature varArgSignature = varArgs == null ? null : varArgs.getSignature();
-            Arguments<RNode> matchedArgs = ArgumentMatcher.matchArguments(target, args, varArgSignature, s3DefaultArguments, RError.ROOTNODE, true);
-            return executeArgs(matchedArgs, frame);
+            Arguments<RNode> matchedArgs = ArgumentMatcher.matchArguments(target, sourceArguments, varArgSignature, s3DefaultArguments, RError.ROOTNODE, true);
+            return executeArgs(matchedArgs.getArguments(), matchedArgs.getSignature(), frame);
         }
     }
 
-    private static final class UninitializedExplicitPrepareArguments extends PrepareArguments {
+    abstract static class PrepareArgumentsExplicit extends PrepareArguments {
 
-        private final RRootNode target;
-        private int depth = CACHE_SIZE;
+        protected final RRootNode target;
+        private final FormalArguments formals;
 
-        UninitializedExplicitPrepareArguments(RRootNode target) {
+        protected PrepareArgumentsExplicit(RRootNode target) {
             this.target = target;
+            this.formals = target.getFormalArguments();
         }
 
-        @Override
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
-            CompilerDirectives.transferToInterpreterAndInvalidate();
-            PrepareArguments next;
-            if (depth-- > 0) {
-                next = new CachedExplicitPrepareArguments(this, target, call, explicitArgs == null ? null : explicitArgs.getSignature());
-            } else {
-                next = new GenericExplicitPrepareArguments(target);
-            }
-            return replace(next).execute(frame, explicitArgs, s3DefaultArguments, call);
+        protected MatchPermutation createArguments(RCallNode call, ArgumentsSignature explicitArgSignature) {
+            return ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, target.getBuiltin());
         }
-    }
-
-    private static final class CachedExplicitPrepareArguments extends PrepareArguments {
-
-        @Child private PrepareArguments next;
-
-        private final MatchPermutation permutation;
-        private final ArgumentsSignature cachedExplicitArgSignature;
-        private final FormalArguments formals;
 
-        CachedExplicitPrepareArguments(PrepareArguments next, RRootNode target, RCallNode call, ArgumentsSignature explicitArgSignature) {
-            this.next = next;
-            formals = target.getFormalArguments();
-            permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, target.getBuiltin());
-            cachedExplicitArgSignature = explicitArgSignature;
+        @Specialization(limit = "CACHE_SIZE", guards = {"cachedExplicitArgSignature == explicitArgs.getSignature()"})
+        public RArgsValuesAndNames prepare(RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, @SuppressWarnings("unused") RCallNode call,
+                        @SuppressWarnings("unused") @Cached("explicitArgs.getSignature()") ArgumentsSignature cachedExplicitArgSignature,
+                        @Cached("createArguments(call, cachedExplicitArgSignature)") MatchPermutation permutation) {
+            return ArgumentMatcher.matchArgumentsEvaluated(permutation, explicitArgs.getArguments(), s3DefaultArguments, formals);
         }
 
-        @Override
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
-            if (cachedExplicitArgSignature == explicitArgs.getSignature()) {
-                return ArgumentMatcher.matchArgumentsEvaluated(permutation, explicitArgs.getArguments(), s3DefaultArguments, formals);
-            }
-            return next.execute(frame, explicitArgs, s3DefaultArguments, call);
+        @Fallback
+        @TruffleBoundary
+        public RArgsValuesAndNames prepareGeneric(RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, @SuppressWarnings("unused") RCallNode call) {
+            // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/
+            return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, RError.ROOTNODE);
         }
     }
 
-    private static final class GenericExplicitPrepareArguments extends PrepareArguments {
-
-        private final RRootNode target;
+    /**
+     * Returns the argument values and corresponding signature. The signature represents the
+     * original call signature reordered in the same way as the arguments. For s3DefaultArguments
+     * motivation see {@link RCallNode#callGroupGeneric}.
+     */
+    public abstract RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames varArgs, S3DefaultArguments s3DefaultArguments, RCallNode call);
 
-        GenericExplicitPrepareArguments(RRootNode target) {
-            this.target = target;
-        }
+    public static PrepareArguments create(RRootNode target, CallArgumentsNode args, boolean noOpt) {
+        return PrepareArgumentsDefaultNodeGen.create(target, args, noOpt);
+    }
 
-        @Override
-        public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) {
-            CompilerDirectives.transferToInterpreter();
-            // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/
-            return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, RError.ROOTNODE);
-        }
+    public static PrepareArguments createExplicit(RRootNode target) {
+        return PrepareArgumentsExplicitNodeGen.create(target);
     }
 }
-- 
GitLab