Generate bytecode for array intrinsics manually

#KT-28285 Fixed
This commit is contained in:
Alexander Udalov
2018-11-22 16:18:49 +01:00
parent dd0b087b92
commit d12e5ddafc
11 changed files with 128 additions and 118 deletions
@@ -4834,12 +4834,7 @@ The "returned" value of try expression with no finally is either the last expres
if (codegen != null) {
codegen.consumeReifiedOperationMarker(typeParameterDescriptor);
}
v.iconst(operationKind.getId());
v.visitLdcInsn(typeParameterAndReificationArgument.getSecond().asString());
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.REIFIED_OPERATION_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(String.class)), false
);
ReifiedTypeInliner.putReifiedOperationMarker(operationKind, typeParameterAndReificationArgument.getSecond(), v);
}
}
@@ -7,13 +7,12 @@ package org.jetbrains.kotlin.codegen.inline
import com.intellij.psi.PsiElement
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
import org.jetbrains.kotlin.codegen.context.ClosureContext
import org.jetbrains.kotlin.codegen.intrinsics.bytecode
import org.jetbrains.kotlin.codegen.intrinsics.classId
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.isReleaseCoroutines
@@ -520,9 +519,13 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
asmMethod: Method
): SMAPAndMethodNode? {
if (isBuiltInArrayIntrinsic(callableDescriptor)) {
val classId = classId
val bytes = state.inlineCache.classBytes.getOrPut(classId) { bytecode }
return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, AsmUtil.asmTypeByClassId(classId))
val body = when {
callableDescriptor is FictitiousArrayConstructor -> IntrinsicArrayConstructors.generateArrayConstructorBody(asmMethod)
callableDescriptor.name.asString() == "emptyArray" -> IntrinsicArrayConstructors.generateEmptyArrayBody(asmMethod)
callableDescriptor.name.asString() == "arrayOf" -> IntrinsicArrayConstructors.generateArrayOfBody(asmMethod)
else -> throw UnsupportedOperationException("Not an array intrinsic: $callableDescriptor")
}
return SMAPAndMethodNode(body, SMAP(listOf(FileMapping.SKIP)))
}
assert(callableDescriptor is DeserializedCallableMemberDescriptor) { "Not a deserialized function or proper: " + callableDescriptor }
@@ -552,7 +555,9 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean {
if (callableDescriptor is FictitiousArrayConstructor) return true
val name = callableDescriptor.name.asString()
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration is BuiltInsPackageFragment
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration.let { container ->
container is PackageFragmentDescriptor && container.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
}
}
/*descriptor is null for captured vars*/
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.Variance
@@ -96,6 +97,16 @@ class ReifiedTypeInliner(
Type.getMethodDescriptor(Type.VOID_TYPE), false
)
}
@JvmStatic
fun putReifiedOperationMarker(operationKind: OperationKind, argument: ReificationArgument, v: InstructionAdapter) {
v.iconst(operationKind.id)
v.visitLdcInsn(argument.asString())
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, REIFIED_OPERATION_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, AsmTypes.JAVA_STRING_TYPE), false
)
}
}
private var maxStackSize = 0
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.codegen.context.CodegenContextUtil
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.intrinsics.classId
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
@@ -75,7 +74,6 @@ private const val INLINE_MARKER_RETURNS_UNIT = 2
private const val INLINE_MARKER_FAKE_CONTINUATION = 3
private const val INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 4
private const val INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 5
private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId)
internal fun getMethodNode(
classData: ByteArray,
@@ -135,11 +133,6 @@ internal fun getMethodNode(
return null
}
if (INTRINSIC_ARRAY_CONSTRUCTOR_TYPE == classType) {
// Don't load source map for intrinsic array constructors
debugInfo[0] = null
}
val smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classType.internalName, lines[0], lines[1])
return SMAPAndMethodNode(node!!, smap)
}
@@ -14,98 +14,112 @@
* limitations under the License.
*/
@file:Suppress("unused", "UNCHECKED_CAST")
package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.MethodNode
// Bodies of methods in this file are used in InlineCodegen to inline array constructors. It is loaded via reflection at runtime.
// TODO: generate the bytecode manually instead, do not depend on the previous compiler working correctly here
internal object IntrinsicArrayConstructors {
fun generateArrayConstructorBody(method: Method): MethodNode {
val node = MethodNode(
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
)
internal val classId: ClassId =
ClassId.topLevel(FqName("org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt"))
val arrayType = method.returnType
val elementType = AsmUtil.correctElementType(arrayType)
internal val bytecode: ByteArray by lazy {
val stream = object {}::class.java.classLoader.getResourceAsStream("${classId.asString()}.class")
stream.readBytes().apply {
stream.close()
val size = StackValue.local(0, Type.INT_TYPE)
val lambda = StackValue.local(1, AsmTypes.FUNCTION1)
val loopIndex = StackValue.local(2, Type.INT_TYPE)
val array = StackValue.local(3, arrayType)
/*
inline fun <reified T> Array(size: Int, init: (Int) -> T): Array<T> {
val result = arrayOfNulls<T>(size)
for (i in 0 until size) {
result[i] = init(i)
}
return result as Array<T>
}
*/
val iv = InstructionAdapter(node)
size.put(iv)
if (elementType.sort == Type.OBJECT) {
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.NEW_ARRAY, ReificationArgument("T", true, 0), iv)
}
iv.newarray(elementType)
array.store(StackValue.onStack(arrayType), iv)
loopIndex.store(StackValue.constant(0), iv)
val begin = Label()
iv.visitLabel(begin)
loopIndex.put(iv)
size.put(iv)
val end = Label()
iv.ificmpge(end)
array.put(iv)
loopIndex.put(iv)
lambda.put(iv)
loopIndex.put(AsmUtil.boxType(Type.INT_TYPE), iv)
iv.invokeinterface(
AsmTypes.FUNCTION1.internalName, OperatorNameConventions.INVOKE.asString(),
Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)
)
StackValue.coerce(AsmTypes.OBJECT_TYPE, elementType, iv)
iv.astore(elementType)
iv.iinc(loopIndex.index, 1)
iv.goTo(begin)
iv.visitLabel(end)
array.put(iv)
iv.areturn(arrayType)
iv.visitMaxs(5, 4)
return node
}
fun generateEmptyArrayBody(method: Method): MethodNode {
val node = MethodNode(
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
)
/*
inline fun <reified T> emptyArray(): Array<T> = arrayOfNulls<T>(0) as Array<T>
*/
val iv = InstructionAdapter(node)
iv.iconst(0)
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.NEW_ARRAY, ReificationArgument("T", true, 0), iv)
iv.newarray(AsmTypes.OBJECT_TYPE)
iv.areturn(AsmTypes.OBJECT_TYPE)
iv.visitMaxs(3, 1)
return node
}
fun generateArrayOfBody(method: Method): MethodNode {
val node = MethodNode(
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
)
/*
inline fun <reified T> arrayOf(vararg elements: T): Array<T> = elements as Array<T>
*/
val iv = InstructionAdapter(node)
iv.load(0, AsmTypes.OBJECT_TYPE)
iv.areturn(AsmTypes.OBJECT_TYPE)
iv.visitMaxs(1, 1)
return node
}
}
private inline fun <reified T> emptyArray(): Array<T> = arrayOfNulls<T>(0) as Array<T>
private inline fun <reified T> arrayOf(vararg elements: T): Array<T> = elements as Array<T>
private inline fun <reified T> Array(size: Int, init: (Int) -> T): Array<T> {
val result = arrayOfNulls<T>(size)
for (i in result.indices) {
result[i] = init(i)
}
return result as Array<T>
}
private inline fun DoubleArray(size: Int, init: (Int) -> Double): DoubleArray {
val result = DoubleArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun FloatArray(size: Int, init: (Int) -> Float): FloatArray {
val result = FloatArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun LongArray(size: Int, init: (Int) -> Long): LongArray {
val result = LongArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun IntArray(size: Int, init: (Int) -> Int): IntArray {
val result = IntArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun CharArray(size: Int, init: (Int) -> Char): CharArray {
val result = CharArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun ShortArray(size: Int, init: (Int) -> Short): ShortArray {
val result = ShortArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun ByteArray(size: Int, init: (Int) -> Byte): ByteArray {
val result = ByteArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
private inline fun BooleanArray(size: Int, init: (Int) -> Boolean): BooleanArray {
val result = BooleanArray(size)
for (i in result.indices) {
result[i] = init(i)
}
return result
}
-2
View File
@@ -115,8 +115,6 @@ messages/**)
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
-2
View File
@@ -100,8 +100,6 @@ messages/**)
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
-2
View File
@@ -116,8 +116,6 @@ messages/**)
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
-2
View File
@@ -101,8 +101,6 @@ messages/**)
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
-2
View File
@@ -116,8 +116,6 @@ messages/**)
-keep class org.jetbrains.kotlin.container.** { *; }
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
-keep class org.jetbrains.kotlin.codegen.extensions.** {
@@ -38,6 +38,8 @@ public class AsmTypes {
public static final Type RESULT_FAILURE = Type.getObjectType("kotlin/Result$Failure");
public static final Type FUNCTION1 = Type.getObjectType("kotlin/jvm/functions/Function1");
public static final Type[] PROPERTY_REFERENCE_IMPL = {
Type.getObjectType("kotlin/jvm/internal/PropertyReference0Impl"),
Type.getObjectType("kotlin/jvm/internal/PropertyReference1Impl"),