< prev index next >

src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Gen.java

Print this page
rev 3493 : Review fixes

@@ -43,11 +43,10 @@
 import com.sun.tools.javac.tree.EndPosTable;
 import com.sun.tools.javac.tree.JCTree.*;
 
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.Kind.*;
-import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
 import static com.sun.tools.javac.code.TypeTag.*;
 import static com.sun.tools.javac.jvm.ByteCodes.*;
 import static com.sun.tools.javac.jvm.CRTFlags.*;
 import static com.sun.tools.javac.main.Option.*;
 import static com.sun.tools.javac.tree.JCTree.Tag.*;

@@ -122,10 +121,44 @@
             : options.isSet(G_CUSTOM, "vars");
         genCrt = options.isSet(XJCOV);
         debugCode = options.isSet("debugcode");
         allowInvokedynamic = target.hasInvokedynamic() || options.isSet("invokedynamic");
         allowBetterNullChecks = target.hasObjects();
+
+        {
+            String concat = options.get("stringConcat");
+            if (target.hasStringConcatFactory()) {
+                if (concat == null) {
+                    concat = "indyWithConstants";
+                }
+
+                switch (concat) {
+                    case "inline":
+                        allowIndyStringConcat = false;
+                        indyStringConcatConstants = false;
+                        break;
+                    case "indy":
+                        allowIndyStringConcat = true;
+                        indyStringConcatConstants = false;
+                        break;
+                    case "indyWithConstants":
+                        allowIndyStringConcat = true;
+                        indyStringConcatConstants = true;
+                        break;
+                    default:
+                        Assert.error("Unknown stringConcat: " + concat);
+                        throw new IllegalStateException("Unknown stringConcat: " + concat);
+                }
+            } else {
+                if (concat != null && !"inline".equals(concat)) {
+                    Assert.error("StringConcatFactory-based string concat is requested on a platform that does not support it.");
+                }
+                allowIndyStringConcat = false;
+                indyStringConcatConstants = false;
+            }
+        }
+
         pool = new Pool(types);
 
         // ignore cldc because we cannot have both stackmap formats
         this.stackMap = StackMapFormat.JSR202;
 

@@ -150,10 +183,12 @@
     private final boolean varDebugInfo;
     private final boolean genCrt;
     private final boolean debugCode;
     private final boolean allowInvokedynamic;
     private final boolean allowBetterNullChecks;
+    private final boolean allowIndyStringConcat;
+    private final boolean indyStringConcatConstants;
 
     /** Default limit of (approximate) size of finalizer to inline.
      *  Zero means always use jsr.  100 or greater means never use
      *  jsr.
      */

@@ -1893,10 +1928,14 @@
 
     public void visitAssignop(JCAssignOp tree) {
         OperatorSymbol operator = (OperatorSymbol) tree.operator;
         Item l;
         if (operator.opcode == string_add) {
+            if (allowIndyStringConcat) {
+                l = genExpr(tree.lhs, tree.lhs.type);
+                emitIndyStringConcat(tree);
+            } else {
             // Generate code to make a string buffer
             makeStringBuffer(tree.pos());
 
             // Generate code for first string, possibly save one
             // copy under buffer

@@ -1912,10 +1951,11 @@
             // Append all other strings to buffer.
             appendStrings(tree.rhs);
 
             // Convert buffer to string.
             bufferToString(tree.pos());
+            }
         } else {
             // Generate code for first expression
             l = genExpr(tree.lhs, tree.lhs.type);
 
             // If we have an increment of -32768 to +32767 of a local

@@ -2024,16 +2064,21 @@
     }
 
     public void visitBinary(JCBinary tree) {
         OperatorSymbol operator = (OperatorSymbol)tree.operator;
         if (operator.opcode == string_add) {
+            if (allowIndyStringConcat) {
+                // Emit indified version of String concat
+                emitIndyStringConcat(tree);
+            } else {
             // Create a string buffer.
             makeStringBuffer(tree.pos());
             // Append all strings to buffer.
             appendStrings(tree);
             // Convert buffer to string.
             bufferToString(tree.pos());
+            }
             result = items.makeStackItem(syms.stringType);
         } else if (tree.hasTag(AND)) {
             CondItem lcond = genCond(tree.lhs, CRT_FLOW_CONTROLLER);
             if (!lcond.isFalse()) {
                 Chain falseJumps = lcond.jumpFalse();

@@ -2064,10 +2109,215 @@
             Item od = genExpr(tree.lhs, operator.type.getParameterTypes().head);
             od.load();
             result = completeBinop(tree.lhs, tree.rhs, operator);
         }
     }
+
+    /**
+     * Maximum number of slots for String Concat call.
+     * JDK's StringConcatFactory does not support more than that.
+     */
+    private static final int MAX_INDY_CONCAT_ARG_SLOTS = 200;
+    private static final String TAG_ARG   = "\u0001";
+    private static final String TAG_CONST = "\u0002";
+
+    List<JCTree> collectStringsRecursive(JCTree tree, List<JCTree> res) {
+        tree = TreeInfo.skipParens(tree);
+        if (tree.hasTag(PLUS) && tree.type.constValue() == null) {
+            JCBinary op = (JCBinary) tree;
+            if (op.operator.kind == MTH &&
+                    ((OperatorSymbol) op.operator).opcode == string_add) {
+                return res
+                        .appendList(collectStringsRecursive(op.lhs, res))
+                        .appendList(collectStringsRecursive(op.rhs, res));
+            }
+        }
+        return res.append(tree);
+    }
+
+    /** Handle the inline assignments, collect all subtrees */
+    private void emitIndyStringConcat(JCAssignOp tree) {
+        List<JCTree> args =
+                List.<JCTree>nil()
+                        .appendList(collectStringsRecursive(tree.lhs, List.nil()))
+                        .appendList(collectStringsRecursive(tree.rhs, List.nil()));
+        emitIndyStringConcat(args, tree.type, tree.pos());
+    }
+
+    /** Handle the string_add operation, collect all subtrees */
+    private void emitIndyStringConcat(JCBinary tree) {
+        List<JCTree> args =
+                List.<JCTree>nil()
+                        .appendList(collectStringsRecursive(tree.lhs, List.nil()))
+                        .appendList(collectStringsRecursive(tree.rhs, List.nil()));
+        emitIndyStringConcat(args, tree.type, tree.pos());
+    }
+
+    /** Emit the indy concat for all these arguments, possibly peeling along the way */
+    private void emitIndyStringConcat(List<JCTree> args, Type type, DiagnosticPosition pos) {
+        int slots = 0;
+        int count = 0;
+
+        // Need to peel, so that neither call has more than acceptable number
+        // of slots for the arguments.
+        ListBuffer<JCTree> cArgs = new ListBuffer<>();
+        for (JCTree t : args) {
+            int needSlots = (t.type.getTag() == LONG || t.type.getTag() == DOUBLE) ? 2 : 1;
+            if (slots + needSlots >= MAX_INDY_CONCAT_ARG_SLOTS) {
+                emitIndyStringConcatOne(cArgs.toList(), type, pos);
+                cArgs.clear();
+                slots = 0;
+                count++;
+            }
+            cArgs.add(t);
+            slots += needSlots;
+        }
+
+        // Flush the tail slice
+        if (!cArgs.isEmpty()) {
+            emitIndyStringConcatOne(cArgs.toList(), type, pos);
+            count++;
+        }
+
+        // More that one peel slice produced: concatenate the results
+        if (count > 1) {
+            emitIndyStringConcatMerge(count, type, pos);
+        }
+    }
+
+    /**
+     * This code builds the recipe, static and dynamic arguments for calling JDK's
+     * StringConcatFactory. See the interface description there.
+     *
+     * We also bypass empty strings, because they have no meaning at this level. This
+     * captures the Java language trick to force String concat with e.g. ("" + int)-like
+     * expression. Down here, we already know we are in String concat business, and do
+     * not require these markers.
+     */
+    private void emitIndyStringConcatOne(List<JCTree> args, Type type, DiagnosticPosition pos) {
+        Assert.check(!args.isEmpty(), "Arguments list is empty");
+
+        StringBuilder recipe = new StringBuilder(args.size());
+        ListBuffer<Type> dynamicArgs = new ListBuffer<>();
+        ListBuffer<Object> staticArgs = new ListBuffer<>();
+
+        if (indyStringConcatConstants) {
+            for (JCTree arg : args) {
+                Object constVal = arg.type.constValue();
+                if ("".equals(constVal)) continue;
+                if (arg.type == syms.botType) {
+                    // Concat the null into the recipe right away
+                    recipe.append("null");
+                } else if (constVal != null) {
+                    // Concat the String representation of the constant, except
+                    // for the case it contains special tags, which requires us
+                    // to expose it as detached constant.
+                    String a = arg.type.stringValue();
+                    if (a.contains(TAG_CONST) || a.contains(TAG_ARG)) {
+                        recipe.append(TAG_CONST);
+                        staticArgs.add(a);
+                    } else {
+                        recipe.append(a);
+                    }
+                } else {
+                    // Ordinary arguments come through the dynamic arguments.
+                    recipe.append(TAG_ARG);
+                    dynamicArgs.add(arg.type);
+                    genExpr(arg, arg.type).load();
+                }
+            }
+        } else {
+            for (JCTree arg : args) {
+                Object constVal = arg.type.constValue();
+                if ("".equals(constVal)) continue;
+                if (arg.type == syms.botType) {
+                    dynamicArgs.add(syms.voidClassType);
+                } else {
+                    dynamicArgs.add(arg.type);
+                }
+                genExpr(arg, arg.type).load();
+            }
+        }
+
+        indyStringConcatDoCall(type, pos, recipe.toString(), staticArgs, dynamicArgs);
+    }
+
+    /** Special version for concatenating the known number of known Strings */
+    private void emitIndyStringConcatMerge(int count, Type type, DiagnosticPosition pos) {
+        Assert.check(count != 0, "Arguments list is empty");
+        Assert.check(count <= MAX_INDY_CONCAT_ARG_SLOTS, "Too many arguments for concatenation");
+
+        // All arguments are assumed to be non-constant Strings
+        StringBuilder recipe = new StringBuilder(count);
+        ListBuffer<Type> argTypes = new ListBuffer<>();
+        for (int c = 0; c < count; c++) {
+            argTypes.append(syms.stringType);
+            recipe.append(TAG_ARG);
+        }
+
+        indyStringConcatDoCall(type, pos, recipe.toString(), new ListBuffer<>(), argTypes);
+    }
+
+    /** Produce the actual invokedynamic call to StringConcatFactory */
+    private void indyStringConcatDoCall(Type type, DiagnosticPosition pos, String recipe, ListBuffer<Object> staticArgs, ListBuffer<Type> dynamicArgTypes) {
+        MethodType indyType = new MethodType(dynamicArgTypes.toList(),
+                type,
+                List.<Type>nil(),
+                syms.methodClass);
+
+        int prevPos = make.pos;
+        try {
+            make.at(pos);
+
+            DynamicMethodSymbol dynSym;
+
+            if (indyStringConcatConstants) {
+                ListBuffer<Type> constTypes = new ListBuffer<>();
+                ListBuffer<Object> constants = new ListBuffer<>();
+                for (Object t : staticArgs) {
+                    constants.add(t);
+                    constTypes.add(syms.stringType);
+                }
+
+                List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
+                        syms.stringType,
+                        syms.methodTypeType)
+                        .append(syms.stringType)
+                        .appendList(constTypes);
+
+                Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, syms.stringConcatFactory,
+                        names.makeConcatWithConstants, bsm_staticArgs, List.<Type>nil());
+
+                dynSym = new DynamicMethodSymbol(names.makeConcatWithConstants,
+                        syms.noSymbol,
+                        ClassFile.REF_invokeStatic,
+                        (MethodSymbol)bsm,
+                        indyType,
+                        List.<Object>of(recipe).appendList(constants).toArray());
+            } else {
+                List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
+                        syms.stringType,
+                        syms.methodTypeType);
+
+                Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, syms.stringConcatFactory,
+                        names.makeConcat, bsm_staticArgs, List.<Type>nil());
+
+                dynSym = new DynamicMethodSymbol(names.makeConcat,
+                        syms.noSymbol,
+                        ClassFile.REF_invokeStatic,
+                        (MethodSymbol)bsm,
+                        indyType,
+                        List.nil().toArray());
+            }
+
+            Item item = items.makeDynamicItem(dynSym);
+            item.invoke();
+        } finally {
+            make.at(prevPos);
+        }
+    }
+
 //where
         /** Make a new string buffer.
          */
         void makeStringBuffer(DiagnosticPosition pos) {
             code.emitop2(new_, makeRef(pos, syms.stringBuilderType));
< prev index next >