< prev index next >

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

Print this page
rev 3493 : Review fixes

*** 43,53 **** 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.*; --- 43,52 ----
*** 122,131 **** --- 121,164 ---- : 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,159 **** --- 183,194 ---- 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,1902 **** --- 1928,1941 ---- 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,1921 **** --- 1951,1961 ---- // 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,2039 **** --- 2064,2084 ---- } 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,2073 **** --- 2109,2323 ---- 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 >