Cache read classes and method nodes for inline
It decrease GENERATE phase nearly for 10%
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
|
import com.intellij.util.containers.SLRUMap
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
|
|
||||||
|
data class MethodId(val containingFqName: FqName, val method: Method)
|
||||||
|
|
||||||
|
class InlineCache {
|
||||||
|
val classBytes: SLRUMap<ClassId, ByteArray> = SLRUMap(30, 20)
|
||||||
|
val methodNodeById: SLRUMap<MethodId, SMAPAndMethodNode> = SLRUMap(60, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <K, V> SLRUMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
|
||||||
|
val value = get(key)
|
||||||
|
return if (value == null) {
|
||||||
|
val answer = defaultValue()
|
||||||
|
put(key, answer)
|
||||||
|
answer
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.codegen.inline;
|
|||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
|
import com.intellij.util.ArrayUtil;
|
||||||
|
import kotlin.jvm.functions.Function0;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
import org.jetbrains.kotlin.backend.common.CodegenUtil;
|
||||||
@@ -64,6 +66,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
|
||||||
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.API;
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
|
||||||
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant;
|
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant;
|
||||||
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
|
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
|
||||||
@@ -184,88 +187,161 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
static SMAPAndMethodNode createMethodNode(
|
static SMAPAndMethodNode createMethodNode(
|
||||||
@NotNull FunctionDescriptor functionDescriptor,
|
@NotNull final FunctionDescriptor functionDescriptor,
|
||||||
@NotNull JvmMethodSignature jvmSignature,
|
@NotNull JvmMethodSignature jvmSignature,
|
||||||
@NotNull ExpressionCodegen codegen,
|
@NotNull ExpressionCodegen codegen,
|
||||||
@NotNull CodegenContext context,
|
@NotNull CodegenContext context,
|
||||||
boolean callDefault,
|
boolean callDefault,
|
||||||
@NotNull GenerationState state) throws IOException {
|
@NotNull final GenerationState state
|
||||||
|
) {
|
||||||
|
|
||||||
KotlinTypeMapper typeMapper = state.getTypeMapper();
|
KotlinTypeMapper typeMapper = state.getTypeMapper();
|
||||||
Method asmMethod = callDefault
|
final Method asmMethod = callDefault
|
||||||
? typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind())
|
? typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind())
|
||||||
: jvmSignature.getAsmMethod();
|
: jvmSignature.getAsmMethod();
|
||||||
|
|
||||||
|
MethodId methodId = new MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.getContainingDeclaration()), asmMethod);
|
||||||
|
|
||||||
|
if (!isBuiltInArrayIntrinsic(functionDescriptor) && !(functionDescriptor instanceof DeserializedSimpleFunctionDescriptor)) {
|
||||||
|
return doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, codegen, context, callDefault, state, asmMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
SMAPAndMethodNode resultInCache =
|
||||||
|
InlineCacheKt
|
||||||
|
.getOrPut(state.getInlineCache().getMethodNodeById(), methodId, new Function0<SMAPAndMethodNode>() {
|
||||||
|
@Override
|
||||||
|
public SMAPAndMethodNode invoke() {
|
||||||
|
return doCreateMethodNodeFromCompiled(functionDescriptor,
|
||||||
|
state, asmMethod);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return resultInCache.copyWithNewNode(cloneMethodNode(resultInCache.getNode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static MethodNode cloneMethodNode(@NotNull MethodNode methodNode) {
|
||||||
|
methodNode.instructions.resetLabels();
|
||||||
|
MethodNode result = new MethodNode(
|
||||||
|
API, methodNode.access, methodNode.name, methodNode.desc, methodNode.signature,
|
||||||
|
methodNode.exceptions.toArray(ArrayUtil.EMPTY_STRING_ARRAY));
|
||||||
|
methodNode.accept(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static SMAPAndMethodNode doCreateMethodNodeFromCompiled(
|
||||||
|
@NotNull FunctionDescriptor functionDescriptor,
|
||||||
|
@NotNull final GenerationState state,
|
||||||
|
@NotNull Method asmMethod
|
||||||
|
) {
|
||||||
|
KotlinTypeMapper typeMapper = state.getTypeMapper();
|
||||||
|
|
||||||
SMAPAndMethodNode nodeAndSMAP;
|
SMAPAndMethodNode nodeAndSMAP;
|
||||||
if (isBuiltInArrayIntrinsic(functionDescriptor)) {
|
if (isBuiltInArrayIntrinsic(functionDescriptor)) {
|
||||||
|
ClassId classId = IntrinsicArrayConstructorsKt.getClassId();
|
||||||
|
byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), classId, new Function0<byte[]>() {
|
||||||
|
@Override
|
||||||
|
public byte[] invoke() {
|
||||||
|
return IntrinsicArrayConstructorsKt.getBytecode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
|
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
|
||||||
IntrinsicArrayConstructorsKt.getBytecode(),
|
bytes,
|
||||||
asmMethod.getName(),
|
asmMethod.getName(),
|
||||||
asmMethod.getDescriptor(),
|
asmMethod.getDescriptor(),
|
||||||
IntrinsicArrayConstructorsKt.getClassId()
|
classId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (nodeAndSMAP == null) {
|
if (nodeAndSMAP == null) {
|
||||||
throw new IllegalStateException("Couldn't obtain array constructor body for " + descriptorName(functionDescriptor));
|
throw new IllegalStateException("Couldn't obtain array constructor body for " + descriptorName(functionDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nodeAndSMAP;
|
||||||
}
|
}
|
||||||
else if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
|
|
||||||
KotlinTypeMapper.ContainingClassesInfo containingClasses = typeMapper.getContainingClassesForDeserializedCallable(
|
|
||||||
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
|
|
||||||
|
|
||||||
ClassId containerId = containingClasses.getImplClassId();
|
assert functionDescriptor instanceof DeserializedSimpleFunctionDescriptor;
|
||||||
VirtualFile file = InlineCodegenUtil.findVirtualFile(state, containerId);
|
|
||||||
if (file == null) {
|
KotlinTypeMapper.ContainingClassesInfo containingClasses = typeMapper.getContainingClassesForDeserializedCallable(
|
||||||
throw new IllegalStateException("Couldn't find declaration file for " + containerId);
|
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
|
||||||
|
|
||||||
|
final ClassId containerId = containingClasses.getImplClassId();
|
||||||
|
|
||||||
|
byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), containerId, new Function0<byte[]>() {
|
||||||
|
@Override
|
||||||
|
public byte[] invoke() {
|
||||||
|
VirtualFile file = InlineCodegenUtil.findVirtualFile(state, containerId);
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalStateException("Couldn't find declaration file for " + containerId);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return file.contentsToByteArray();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
|
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
|
||||||
file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor(), containerId
|
bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nodeAndSMAP == null) {
|
||||||
|
throw new IllegalStateException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeAndSMAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static SMAPAndMethodNode doCreateMethodNodeFromSource(
|
||||||
|
@NotNull FunctionDescriptor functionDescriptor,
|
||||||
|
@NotNull JvmMethodSignature jvmSignature,
|
||||||
|
@NotNull ExpressionCodegen codegen,
|
||||||
|
@NotNull CodegenContext context,
|
||||||
|
boolean callDefault,
|
||||||
|
@NotNull GenerationState state,
|
||||||
|
@NotNull Method asmMethod
|
||||||
|
) {
|
||||||
|
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
||||||
|
|
||||||
|
if (!(element instanceof KtNamedFunction)) {
|
||||||
|
throw new IllegalStateException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
|
||||||
|
}
|
||||||
|
KtNamedFunction inliningFunction = (KtNamedFunction) element;
|
||||||
|
|
||||||
|
MethodNode node = new MethodNode(InlineCodegenUtil.API,
|
||||||
|
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
|
||||||
|
asmMethod.getName(),
|
||||||
|
asmMethod.getDescriptor(),
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
//for maxLocals calculation
|
||||||
|
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
|
||||||
|
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
|
||||||
|
|
||||||
|
SMAP smap;
|
||||||
|
if (callDefault) {
|
||||||
|
Type implementationOwner = state.getTypeMapper().mapImplementationOwner(functionDescriptor);
|
||||||
|
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
|
||||||
|
(FieldOwnerContext) methodContext.getParentContext(),
|
||||||
|
implementationOwner.getInternalName());
|
||||||
|
FunctionCodegen.generateDefaultImplBody(
|
||||||
|
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
||||||
|
inliningFunction, parentCodegen, asmMethod
|
||||||
);
|
);
|
||||||
|
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
|
||||||
if (nodeAndSMAP == null) {
|
|
||||||
throw new IllegalStateException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
|
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen, state);
|
||||||
|
|
||||||
if (!(element instanceof KtNamedFunction)) {
|
|
||||||
throw new IllegalStateException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
|
|
||||||
}
|
|
||||||
KtNamedFunction inliningFunction = (KtNamedFunction) element;
|
|
||||||
|
|
||||||
MethodNode node = new MethodNode(InlineCodegenUtil.API,
|
|
||||||
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
|
|
||||||
asmMethod.getName(),
|
|
||||||
asmMethod.getDescriptor(),
|
|
||||||
null,
|
|
||||||
null);
|
|
||||||
|
|
||||||
//for maxLocals calculation
|
|
||||||
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
|
|
||||||
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
|
|
||||||
|
|
||||||
SMAP smap;
|
|
||||||
if (callDefault) {
|
|
||||||
Type implementationOwner = typeMapper.mapImplementationOwner(functionDescriptor);
|
|
||||||
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
|
|
||||||
(FieldOwnerContext) methodContext.getParentContext(),
|
|
||||||
implementationOwner.getInternalName());
|
|
||||||
FunctionCodegen.generateDefaultImplBody(
|
|
||||||
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
|
|
||||||
inliningFunction, parentCodegen, asmMethod
|
|
||||||
);
|
|
||||||
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeAndSMAP = new SMAPAndMethodNode(node, smap);
|
|
||||||
maxCalcAdapter.visitMaxs(-1, -1);
|
|
||||||
maxCalcAdapter.visitEnd();
|
|
||||||
}
|
}
|
||||||
return nodeAndSMAP;
|
maxCalcAdapter.visitMaxs(-1, -1);
|
||||||
|
maxCalcAdapter.visitEnd();
|
||||||
|
|
||||||
|
return new SMAPAndMethodNode(node, smap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isBuiltInArrayIntrinsic(@NotNull FunctionDescriptor functionDescriptor) {
|
private static boolean isBuiltInArrayIntrinsic(@NotNull FunctionDescriptor functionDescriptor) {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public class InlineCodegenUtil {
|
|||||||
final String methodName,
|
final String methodName,
|
||||||
final String methodDescriptor,
|
final String methodDescriptor,
|
||||||
ClassId classId
|
ClassId classId
|
||||||
) throws IOException {
|
) {
|
||||||
ClassReader cr = new ClassReader(classData);
|
ClassReader cr = new ClassReader(classData);
|
||||||
final MethodNode[] node = new MethodNode[1];
|
final MethodNode[] node = new MethodNode[1];
|
||||||
final String[] debugInfo = new String[2];
|
final String[] debugInfo = new String[2];
|
||||||
|
|||||||
@@ -23,19 +23,22 @@ import java.util.*
|
|||||||
|
|
||||||
//TODO comment
|
//TODO comment
|
||||||
class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) {
|
class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) {
|
||||||
private val lineNumbers =
|
val ranges = createLineNumberSequence(node, classSMAP).map { it.mapper }.distinct().toList()
|
||||||
InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { node ->
|
|
||||||
val index = classSMAP.intervals.binarySearch(RangeMapping(node.line, node.line, 1), Comparator {
|
|
||||||
value, key ->
|
|
||||||
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
|
|
||||||
})
|
|
||||||
if (index < 0) {
|
|
||||||
error("Unmapped label in inlined function $node ${node.line}")
|
|
||||||
}
|
|
||||||
LabelAndMapping(node, classSMAP.intervals[index])
|
|
||||||
}.toList()
|
|
||||||
|
|
||||||
val ranges = lineNumbers.asSequence().map { it.mapper }.distinct().toList()
|
fun copyWithNewNode(newMethodNode: MethodNode) = SMAPAndMethodNode(newMethodNode, classSMAP)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence<LabelAndMapping> {
|
||||||
|
return InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { node ->
|
||||||
|
val index = classSMAP.intervals.binarySearch(RangeMapping(node.line, node.line, 1), Comparator {
|
||||||
|
value, key ->
|
||||||
|
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
|
||||||
|
})
|
||||||
|
if (index < 0) {
|
||||||
|
error("Unmapped label in inlined function $node ${node.line}")
|
||||||
|
}
|
||||||
|
LabelAndMapping(node, classSMAP.intervals[index])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping)
|
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
|||||||
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
||||||
import org.jetbrains.kotlin.codegen.context.RootContext
|
import org.jetbrains.kotlin.codegen.context.RootContext
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.InlineCache
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
|
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
|
||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
@@ -89,6 +90,7 @@ class GenerationState @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val fileClassesProvider: CodegenFileClassesProvider = CodegenFileClassesProvider()
|
val fileClassesProvider: CodegenFileClassesProvider = CodegenFileClassesProvider()
|
||||||
|
val inlineCache: InlineCache = InlineCache()
|
||||||
|
|
||||||
private fun getIncrementalCacheForThisTarget() =
|
private fun getIncrementalCacheForThisTarget() =
|
||||||
if (incrementalCompilationComponents != null && targetId != null)
|
if (incrementalCompilationComponents != null && targetId != null)
|
||||||
|
|||||||
Reference in New Issue
Block a user