Add runtime string concat options. Some renaming

This commit is contained in:
Mikhael Bogdanov
2020-09-29 08:47:06 +02:00
parent 04012951c1
commit c329c22630
20 changed files with 97 additions and 24 deletions
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.codegen; package org.jetbrains.kotlin.codegen;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
@@ -52,7 +51,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass;
@@ -495,12 +493,12 @@ public class DescriptorAsmUtil {
return index; return index;
} }
public static void genInvokeAppendMethod(@NotNull StringAppendGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) { public static void genInvokeAppendMethod(@NotNull StringConcatGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) {
genInvokeAppendMethod(generator, type, kotlinType, null); genInvokeAppendMethod(generator, type, kotlinType, null);
} }
public static void genInvokeAppendMethod( public static void genInvokeAppendMethod(
@NotNull StringAppendGenerator generator, @NotNull StringConcatGenerator generator,
@NotNull Type type, @NotNull Type type,
@Nullable KotlinType kotlinType, @Nullable KotlinType kotlinType,
@Nullable KotlinTypeMapper typeMapper @Nullable KotlinTypeMapper typeMapper
@@ -925,7 +925,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
else { else {
return StackValue.operation(type, v -> { return StackValue.operation(type, v -> {
StringAppendGenerator generator = StringAppendGenerator.Companion.create(state, v); StringConcatGenerator generator = StringConcatGenerator.Companion.create(state, v);
generator.genStringBuilderConstructorIfNeded(); generator.genStringBuilderConstructorIfNeded();
invokeAppendForEntries(generator, entries); invokeAppendForEntries(generator, entries);
generator.genToString(); generator.genToString();
@@ -934,7 +934,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
} }
private void invokeAppendForEntries(StringAppendGenerator generator, List<StringTemplateEntry> entries) { private void invokeAppendForEntries(StringConcatGenerator generator, List<StringTemplateEntry> entries) {
for (StringTemplateEntry entry : entries) { for (StringTemplateEntry entry : entries) {
if (entry instanceof StringTemplateEntry.Expression) { if (entry instanceof StringTemplateEntry.Expression) {
invokeAppend(generator, ((StringTemplateEntry.Expression) entry).expression); invokeAppend(generator, ((StringTemplateEntry.Expression) entry).expression);
@@ -4300,7 +4300,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
} }
public void invokeAppend(StringAppendGenerator generator, KtExpression expr) { public void invokeAppend(StringConcatGenerator generator, KtExpression expr) {
expr = KtPsiUtil.safeDeparenthesize(expr); expr = KtPsiUtil.safeDeparenthesize(expr);
ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr); ConstantValue<?> compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr);
@@ -102,7 +102,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator {
mv.visitCode(); mv.visitCode();
StringAppendGenerator generator = StringAppendGenerator.Companion.create(generationState, iv); StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv);
generator.genStringBuilderConstructorIfNeded(); generator.genStringBuilderConstructorIfNeded();
boolean first = true; boolean first = true;
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.codegen
import com.google.common.collect.Sets import com.google.common.collect.Sets
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.JvmRuntimeStringConcat
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
import org.jetbrains.org.objectweb.asm.Handle import org.jetbrains.org.objectweb.asm.Handle
@@ -16,14 +16,14 @@ import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import java.lang.StringBuilder import java.lang.StringBuilder
class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAdapter) { class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: InstructionAdapter) {
private val template = StringBuilder("") private val template = StringBuilder("")
private val paramTypes = arrayListOf<Type>() private val paramTypes = arrayListOf<Type>()
@JvmOverloads @JvmOverloads
fun genStringBuilderConstructorIfNeded(swap: Boolean = false) { fun genStringBuilderConstructorIfNeded(swap: Boolean = false) {
if (useInvokeDynamic) return if (mode.isDynamic) return
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder") mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder")
mv.dup() mv.dup()
mv.invokespecial("java/lang/StringBuilder", "<init>", "()V", false) mv.invokespecial("java/lang/StringBuilder", "<init>", "()V", false)
@@ -33,7 +33,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd
} }
fun addCharConstant(value: Char) { fun addCharConstant(value: Char) {
if (!useInvokeDynamic) { if (!mode.isDynamic) {
mv.iconst(value.toInt()) mv.iconst(value.toInt())
invokeAppend(Type.CHAR_TYPE) invokeAppend(Type.CHAR_TYPE)
} else { } else {
@@ -42,7 +42,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd
} }
fun addStringConstant(value: String) { fun addStringConstant(value: String) {
if (!useInvokeDynamic) { if (!mode.isDynamic) {
mv.aconst(value) mv.aconst(value)
invokeAppend(JAVA_STRING_TYPE) invokeAppend(JAVA_STRING_TYPE)
} else { } else {
@@ -51,7 +51,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd
} }
fun invokeAppend(type: Type) { fun invokeAppend(type: Type) {
if (!useInvokeDynamic) { if (!mode.isDynamic) {
mv.invokevirtual( mv.invokevirtual(
"java/lang/StringBuilder", "java/lang/StringBuilder",
"append", "append",
@@ -71,7 +71,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd
} }
fun genToString() { fun genToString() {
if (!useInvokeDynamic) { if (!mode.isDynamic) {
mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
} else { } else {
//if state was flushed in `invokeAppend` do nothing //if state was flushed in `invokeAppend` do nothing
@@ -114,7 +114,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd
} }
fun create(state: GenerationState, mv: InstructionAdapter) = fun create(state: GenerationState, mv: InstructionAdapter) =
StringAppendGenerator(/*TODO: add flag*/state.target.bytecodeVersion >= JvmTarget.JVM_9.bytecodeVersion, mv) StringConcatGenerator(state.runtimeStringConcat, mv)
} }
} }
@@ -39,7 +39,7 @@ class Concat : IntrinsicMethod() {
arguments: List<KtExpression>, arguments: List<KtExpression>,
receiver: StackValue receiver: StackValue
): Type { ): Type {
val generator = StringAppendGenerator.create(codegen.state, v) val generator = StringConcatGenerator.create(codegen.state, v)
if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) { if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) {
// LHS + RHS // LHS + RHS
generator.genStringBuilderConstructorIfNeded() generator.genStringBuilderConstructorIfNeded()
@@ -59,7 +59,7 @@ class Concat : IntrinsicMethod() {
override fun toCallable(method: CallableMethod): Callable = override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(method) { object : IntrinsicCallable(method) {
lateinit var generator: StringAppendGenerator lateinit var generator: StringConcatGenerator
override fun invokeMethodWithArguments( override fun invokeMethodWithArguments(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiver: StackValue, receiver: StackValue,
@@ -83,8 +83,8 @@ class Concat : IntrinsicMethod() {
} }
override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) { override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) {
generator = StringAppendGenerator.create(state, v) generator = StringConcatGenerator.create(state, v)
if (!generator.useInvokeDynamic) { if (!generator.mode.isDynamic) {
v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, JAVA_STRING_TYPE, "java/lang/StringBuilder") v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, JAVA_STRING_TYPE, "java/lang/StringBuilder")
v.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false) v.invokespecial("java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false)
} else { } else {
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.codegen.intrinsics package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -25,19 +24,19 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class StringPlus : IntrinsicMethod() { class StringPlus : IntrinsicMethod() {
override fun toCallable(method: CallableMethod): Callable = override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(method) { object : IntrinsicCallable(method) {
private lateinit var generator: StringAppendGenerator private lateinit var generator: StringConcatGenerator
override fun invokeMethodWithArguments( override fun invokeMethodWithArguments(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiver: StackValue, receiver: StackValue,
codegen: ExpressionCodegen codegen: ExpressionCodegen
): StackValue { ): StackValue {
generator = StringAppendGenerator.create(codegen.state, codegen.v) generator = StringConcatGenerator.create(codegen.state, codegen.v)
return super.invokeMethodWithArguments(resolvedCall, receiver, codegen) return super.invokeMethodWithArguments(resolvedCall, receiver, codegen)
} }
override fun genInvokeInstruction(v: InstructionAdapter) { override fun genInvokeInstruction(v: InstructionAdapter) {
if (!generator.useInvokeDynamic) { if (!generator.mode.isDynamic) {
v.invokestatic( v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, "stringPlus", IntrinsicMethods.INTRINSICS_CLASS_NAME, "stringPlus",
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;", false "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;", false
@@ -191,6 +191,10 @@ class GenerationState private constructor(
val languageVersionSettings = configuration.languageVersionSettings val languageVersionSettings = configuration.languageVersionSettings
val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT
val runtimeStringConcat =
if (target.bytecodeVersion >= JvmTarget.JVM_9.bytecodeVersion)
configuration.get(JVMConfigurationKeys.RUNTIME_STRING_CONCAT) ?: JvmRuntimeStringConcat.DISABLE
else JvmRuntimeStringConcat.DISABLE
val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module) val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module)
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
@@ -334,6 +334,16 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
) )
var emitJvmTypeAnnotations: Boolean by FreezableVar(false) var emitJvmTypeAnnotations: Boolean by FreezableVar(false)
@Argument(
value = "-Xruntime-string-concat",
valueDescription = "{disable|enable|indy}",
description = """Switch a way in which string concatenation is performed.
-Xruntime-string-concat=enable Performs string concatenation via `invokedynamic` 'makeConcatWithConstants'. Works only with `-jvm-target 9` or greater
-Xruntime-string-concat=indy Performs string concatenation via `invokedynamic` 'makeConcat'. Works only with `-jvm-target 9` or greater
-Xruntime-string-concat=disable Performs string concatenation via `StringBuilder`"""
)
var runtimeStringConcat: String? by NullableStringFreezableVar(JvmRuntimeStringConcat.DISABLE.name.toLowerCase())
@Argument( @Argument(
value = "-Xklib", value = "-Xklib",
valueDescription = "<path>", valueDescription = "<path>",
@@ -49,6 +49,24 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
} }
} }
if (arguments.runtimeStringConcat != null) {
val runtimeStringConcat = JvmRuntimeStringConcat.fromString(arguments.runtimeStringConcat!!)
if (runtimeStringConcat != null) {
put(JVMConfigurationKeys.RUNTIME_STRING_CONCAT, runtimeStringConcat)
if (jvmTarget.bytecodeVersion < JvmTarget.JVM_9.bytecodeVersion && runtimeStringConcat != JvmRuntimeStringConcat.DISABLE) {
messageCollector.report(
WARNING,
"`-Xruntime-string-concat=${arguments.runtimeStringConcat}` does nothing with JVM target `${jvmTarget.description}`."
)
}
} else {
messageCollector.report(
ERROR, "Unknown `runtime-string-concat` mode: ${arguments.jvmTarget}\n" +
"Supported versions: ${JvmRuntimeStringConcat.values().joinToString { it.name.toLowerCase() }}"
)
}
}
addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, arguments.additionalJavaModules?.asList()) addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, arguments.additionalJavaModules?.asList())
} }
@@ -111,6 +111,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> EMIT_JVM_TYPE_ANNOTATIONS = public static final CompilerConfigurationKey<Boolean> EMIT_JVM_TYPE_ANNOTATIONS =
CompilerConfigurationKey.create("Emit JVM type annotations in bytecode"); CompilerConfigurationKey.create("Emit JVM type annotations in bytecode");
public static final CompilerConfigurationKey<JvmRuntimeStringConcat> RUNTIME_STRING_CONCAT =
CompilerConfigurationKey.create("Specifies string concatenation scheme");
public static final CompilerConfigurationKey<List<String>> KLIB_PATHS = public static final CompilerConfigurationKey<List<String>> KLIB_PATHS =
CompilerConfigurationKey.create("Paths to .klib libraries"); CompilerConfigurationKey.create("Paths to .klib libraries");
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.config
enum class JvmRuntimeStringConcat {
DISABLE,
ENABLE, // makeConcatWithConstants
INDY; // makeConcat
val isDynamic
get() = this != DISABLE
companion object {
@JvmStatic
fun fromString(string: String) = values().find { it.name == string.toUpperCase() }
}
}
+5
View File
@@ -81,6 +81,11 @@ where advanced options include:
profilerPath is a path to libasyncProfiler.so profilerPath is a path to libasyncProfiler.so
Example: -Xprofile=<PATH_TO_ASYNC_PROFILER>/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000:<SNAPSHOT_DIR_PATH> Example: -Xprofile=<PATH_TO_ASYNC_PROFILER>/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000:<SNAPSHOT_DIR_PATH>
-Xrepeat=<number> Debug option: Repeats modules compilation <number> times -Xrepeat=<number> Debug option: Repeats modules compilation <number> times
-Xruntime-string-concat={disable|enable|indy}
Switch a way in which string concatenation is performed.
-Xruntime-string-concat=enable Performs string concatenation via `invokedynamic` 'makeConcatWithConstants'. Works only with `-jvm-target 9` or greater
-Xruntime-string-concat=indy Performs string concatenation via `invokedynamic` 'makeConcat'. Works only with `-jvm-target 9` or greater
-Xruntime-string-concat=disable Performs string concatenation via `StringBuilder`
-Xsanitize-parentheses Transform '(' and ')' in method names to some other character sequence. -Xsanitize-parentheses Transform '(' and ')' in method names to some other character sequence.
This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used to workaround This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used to workaround
problems with parentheses in identifiers on certain platforms problems with parentheses in identifiers on certain platforms
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
class A class A
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
fun box() { fun box() {
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
fun box() { fun box() {
val z = "0" val z = "0"
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
class A class A
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
fun box(a: String, b: String?) { fun box(a: String, b: String?) {
val sb = StringBuilder(); val sb = StringBuilder();
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
fun box(): String { fun box(): String {
val z = "0" val z = "0"
@@ -1,3 +1,4 @@
// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable
// JVM_TARGET: 9 // JVM_TARGET: 9
fun box(): String { fun box(): String {
val z = "0" val z = "0"
@@ -157,6 +157,7 @@ abstract class KotlinBaseTest<F : KotlinBaseTest.TestFile> : KtUsefulTestCase()
"CONSTRUCTOR_CALL_NORMALIZATION_MODE=([a-zA-Z_\\-0-9]*)" "CONSTRUCTOR_CALL_NORMALIZATION_MODE=([a-zA-Z_\\-0-9]*)"
) )
private val ASSERTIONS_MODE_FLAG_PATTERN = Pattern.compile("ASSERTIONS_MODE=([a-zA-Z_0-9-]*)") private val ASSERTIONS_MODE_FLAG_PATTERN = Pattern.compile("ASSERTIONS_MODE=([a-zA-Z_0-9-]*)")
private val RUNTIME_STRING_CONCAT = Pattern.compile("RUNTIME_STRING_CONCAT=([a-zA-Z_0-9-]*)")
private fun tryApplyBooleanFlag( private fun tryApplyBooleanFlag(
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
@@ -294,6 +295,14 @@ abstract class KotlinBaseTest<F : KotlinBaseTest.TestFile> : KtUsefulTestCase()
?: error("Wrong ASSERTIONS_MODE value: $flagValueString") ?: error("Wrong ASSERTIONS_MODE value: $flagValueString")
configuration.put(JVMConfigurationKeys.ASSERTIONS_MODE, mode) configuration.put(JVMConfigurationKeys.ASSERTIONS_MODE, mode)
} }
m = RUNTIME_STRING_CONCAT.matcher(flag)
if (m.matches()) {
val flagValueString = m.group(1)
val mode = JvmRuntimeStringConcat.fromString(flagValueString)
?: error("Wrong RUNTIME_STRING_CONCAT value: $flagValueString")
configuration.put(JVMConfigurationKeys.RUNTIME_STRING_CONCAT, mode)
}
} }
} }