Skip to content

Commit 1d0c37f

Browse files
committed
Cache read classes and method nodes for inline
It decrease GENERATE phase nearly for 10%
1 parent 26081bf commit 1d0c37f

File tree

5 files changed

+189
-68
lines changed

5 files changed

+189
-68
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2010-2016 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.codegen.inline
18+
19+
import com.intellij.util.containers.SLRUMap
20+
import org.jetbrains.kotlin.name.ClassId
21+
import org.jetbrains.kotlin.name.FqName
22+
import org.jetbrains.org.objectweb.asm.commons.Method
23+
24+
data class MethodId(val containingFqName: FqName, val method: Method)
25+
26+
class InlineCache {
27+
val classBytes: SLRUMap<ClassId, ByteArray> = SLRUMap(30, 20)
28+
val methodNodeById: SLRUMap<MethodId, SMAPAndMethodNode> = SLRUMap(60, 50)
29+
}
30+
31+
inline fun <K, V> SLRUMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
32+
val value = get(key)
33+
return if (value == null) {
34+
val answer = defaultValue()
35+
put(key, answer)
36+
answer
37+
} else {
38+
value
39+
}
40+
}

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java

Lines changed: 131 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.intellij.openapi.vfs.VirtualFile;
2020
import com.intellij.psi.PsiElement;
2121
import com.intellij.psi.PsiFile;
22+
import com.intellij.util.ArrayUtil;
23+
import kotlin.jvm.functions.Function0;
2224
import org.jetbrains.annotations.NotNull;
2325
import org.jetbrains.annotations.Nullable;
2426
import org.jetbrains.kotlin.backend.common.CodegenUtil;
@@ -64,6 +66,7 @@
6466

6567
import static org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags;
6668
import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive;
69+
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.API;
6770
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker;
6871
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant;
6972
import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral;
@@ -184,90 +187,163 @@ private SMAPAndMethodNode createMethodNode(boolean callDefault) throws IOExcepti
184187

185188
@NotNull
186189
static SMAPAndMethodNode createMethodNode(
187-
@NotNull FunctionDescriptor functionDescriptor,
190+
@NotNull final FunctionDescriptor functionDescriptor,
188191
@NotNull JvmMethodSignature jvmSignature,
189192
@NotNull ExpressionCodegen codegen,
190193
@NotNull CodegenContext context,
191194
boolean callDefault,
192-
@NotNull GenerationState state) throws IOException {
195+
@NotNull final GenerationState state
196+
) {
197+
193198
KotlinTypeMapper typeMapper = state.getTypeMapper();
194-
Method asmMethod = callDefault
199+
final Method asmMethod = callDefault
195200
? typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind())
196201
: jvmSignature.getAsmMethod();
197202

203+
MethodId methodId = new MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.getContainingDeclaration()), asmMethod);
204+
205+
if (!isBuiltInArrayIntrinsic(functionDescriptor) && !(functionDescriptor instanceof DeserializedSimpleFunctionDescriptor)) {
206+
return doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, codegen, context, callDefault, state, asmMethod);
207+
}
208+
209+
SMAPAndMethodNode resultInCache =
210+
InlineCacheKt
211+
.getOrPut(state.getInlineCache().getMethodNodeById(), methodId, new Function0<SMAPAndMethodNode>() {
212+
@Override
213+
public SMAPAndMethodNode invoke() {
214+
return doCreateMethodNodeFromCompiled(functionDescriptor,
215+
state, asmMethod);
216+
}
217+
});
218+
219+
return resultInCache.copyWithNewNode(cloneMethodNode(resultInCache.getNode()));
220+
}
221+
222+
@NotNull
223+
private static MethodNode cloneMethodNode(@NotNull MethodNode methodNode) {
224+
methodNode.instructions.resetLabels();
225+
MethodNode result = new MethodNode(
226+
API, methodNode.access, methodNode.name, methodNode.desc, methodNode.signature,
227+
methodNode.exceptions.toArray(ArrayUtil.EMPTY_STRING_ARRAY));
228+
methodNode.accept(result);
229+
return result;
230+
}
231+
232+
@NotNull
233+
private static SMAPAndMethodNode doCreateMethodNodeFromCompiled(
234+
@NotNull FunctionDescriptor functionDescriptor,
235+
@NotNull final GenerationState state,
236+
@NotNull Method asmMethod
237+
) {
238+
KotlinTypeMapper typeMapper = state.getTypeMapper();
239+
198240
SMAPAndMethodNode nodeAndSMAP;
199241
if (isBuiltInArrayIntrinsic(functionDescriptor)) {
242+
ClassId classId = IntrinsicArrayConstructorsKt.getClassId();
243+
byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), classId, new Function0<byte[]>() {
244+
@Override
245+
public byte[] invoke() {
246+
return IntrinsicArrayConstructorsKt.getBytecode();
247+
}
248+
});
249+
200250
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
201-
IntrinsicArrayConstructorsKt.getBytecode(),
251+
bytes,
202252
asmMethod.getName(),
203253
asmMethod.getDescriptor(),
204-
IntrinsicArrayConstructorsKt.getClassId()
254+
classId
205255
);
206256

207257
if (nodeAndSMAP == null) {
208258
throw new IllegalStateException("Couldn't obtain array constructor body for " + descriptorName(functionDescriptor));
209259
}
260+
261+
return nodeAndSMAP;
210262
}
211-
else if (functionDescriptor instanceof DeserializedSimpleFunctionDescriptor) {
212-
KotlinTypeMapper.ContainingClassesInfo containingClasses = typeMapper.getContainingClassesForDeserializedCallable(
213-
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
214263

215-
ClassId containerId = containingClasses.getImplClassId();
216-
VirtualFile file = InlineCodegenUtil.findVirtualFile(state, containerId);
217-
if (file == null) {
218-
throw new IllegalStateException("Couldn't find declaration file for " + containerId);
219-
}
264+
assert functionDescriptor instanceof DeserializedSimpleFunctionDescriptor;
220265

221-
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
222-
file.contentsToByteArray(), asmMethod.getName(), asmMethod.getDescriptor(), containerId
223-
);
266+
KotlinTypeMapper.ContainingClassesInfo containingClasses = typeMapper.getContainingClassesForDeserializedCallable(
267+
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
224268

225-
if (nodeAndSMAP == null) {
226-
throw new IllegalStateException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
227-
}
228-
}
229-
else {
230-
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
269+
final ClassId containerId = containingClasses.getImplClassId();
231270

232-
if (!(element instanceof KtNamedFunction)) {
233-
throw new IllegalStateException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
234-
}
235-
KtNamedFunction inliningFunction = (KtNamedFunction) element;
236-
237-
MethodNode node = new MethodNode(InlineCodegenUtil.API,
238-
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
239-
asmMethod.getName(),
240-
asmMethod.getDescriptor(),
241-
null,
242-
null);
243-
244-
//for maxLocals calculation
245-
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
246-
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
247-
248-
SMAP smap;
249-
if (callDefault) {
250-
Type implementationOwner = typeMapper.mapImplementationOwner(functionDescriptor);
251-
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
252-
(FieldOwnerContext) methodContext.getParentContext(),
253-
implementationOwner.getInternalName());
254-
FunctionCodegen.generateDefaultImplBody(
255-
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
256-
inliningFunction, parentCodegen, asmMethod
257-
);
258-
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
259-
}
260-
else {
261-
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen, state);
271+
byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), containerId, new Function0<byte[]>() {
272+
@Override
273+
public byte[] invoke() {
274+
VirtualFile file = InlineCodegenUtil.findVirtualFile(state, containerId);
275+
if (file == null) {
276+
throw new IllegalStateException("Couldn't find declaration file for " + containerId);
277+
}
278+
try {
279+
return file.contentsToByteArray();
280+
}
281+
catch (IOException e) {
282+
throw new RuntimeException(e);
283+
}
262284
}
285+
});
263286

264-
nodeAndSMAP = new SMAPAndMethodNode(node, smap);
265-
maxCalcAdapter.visitMaxs(-1, -1);
266-
maxCalcAdapter.visitEnd();
287+
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
288+
bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId
289+
);
290+
291+
if (nodeAndSMAP == null) {
292+
throw new IllegalStateException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
267293
}
294+
268295
return nodeAndSMAP;
269296
}
270297

298+
@NotNull
299+
private static SMAPAndMethodNode doCreateMethodNodeFromSource(
300+
@NotNull FunctionDescriptor functionDescriptor,
301+
@NotNull JvmMethodSignature jvmSignature,
302+
@NotNull ExpressionCodegen codegen,
303+
@NotNull CodegenContext context,
304+
boolean callDefault,
305+
@NotNull GenerationState state,
306+
@NotNull Method asmMethod
307+
) {
308+
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
309+
310+
if (!(element instanceof KtNamedFunction)) {
311+
throw new IllegalStateException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
312+
}
313+
KtNamedFunction inliningFunction = (KtNamedFunction) element;
314+
315+
MethodNode node = new MethodNode(InlineCodegenUtil.API,
316+
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
317+
asmMethod.getName(),
318+
asmMethod.getDescriptor(),
319+
null,
320+
null);
321+
322+
//for maxLocals calculation
323+
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
324+
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
325+
326+
SMAP smap;
327+
if (callDefault) {
328+
Type implementationOwner = state.getTypeMapper().mapImplementationOwner(functionDescriptor);
329+
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
330+
(FieldOwnerContext) methodContext.getParentContext(),
331+
implementationOwner.getInternalName());
332+
FunctionCodegen.generateDefaultImplBody(
333+
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
334+
inliningFunction, parentCodegen, asmMethod
335+
);
336+
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
337+
}
338+
else {
339+
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen, state);
340+
}
341+
maxCalcAdapter.visitMaxs(-1, -1);
342+
maxCalcAdapter.visitEnd();
343+
344+
return new SMAPAndMethodNode(node, smap);
345+
}
346+
271347
private static boolean isBuiltInArrayIntrinsic(@NotNull FunctionDescriptor functionDescriptor) {
272348
if (functionDescriptor instanceof FictitiousArrayConstructor) return true;
273349
String name = functionDescriptor.getName().asString();

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegenUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public static SMAPAndMethodNode getMethodNode(
8686
final String methodName,
8787
final String methodDescriptor,
8888
ClassId classId
89-
) throws IOException {
89+
) {
9090
ClassReader cr = new ClassReader(classData);
9191
final MethodNode[] node = new MethodNode[1];
9292
final String[] debugInfo = new String[2];

compiler/backend/src/org/jetbrains/kotlin/codegen/inline/SMAPAndMethodNode.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,22 @@ import java.util.*
2323

2424
//TODO comment
2525
class SMAPAndMethodNode(val node: MethodNode, val classSMAP: SMAP) {
26-
private val lineNumbers =
27-
InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { node ->
28-
val index = classSMAP.intervals.binarySearch(RangeMapping(node.line, node.line, 1), Comparator {
29-
value, key ->
30-
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
31-
})
32-
if (index < 0) {
33-
error("Unmapped label in inlined function $node ${node.line}")
34-
}
35-
LabelAndMapping(node, classSMAP.intervals[index])
36-
}.toList()
26+
val ranges = createLineNumberSequence(node, classSMAP).map { it.mapper }.distinct().toList()
3727

38-
val ranges = lineNumbers.asSequence().map { it.mapper }.distinct().toList()
28+
fun copyWithNewNode(newMethodNode: MethodNode) = SMAPAndMethodNode(newMethodNode, classSMAP)
29+
}
30+
31+
private fun createLineNumberSequence(node: MethodNode, classSMAP: SMAP): Sequence<LabelAndMapping> {
32+
return InsnSequence(node.instructions.first, null).filterIsInstance<LineNumberNode>().map { node ->
33+
val index = classSMAP.intervals.binarySearch(RangeMapping(node.line, node.line, 1), Comparator {
34+
value, key ->
35+
if (key.dest in value) 0 else RangeMapping.Comparator.compare(value, key)
36+
})
37+
if (index < 0) {
38+
error("Unmapped label in inlined function $node ${node.line}")
39+
}
40+
LabelAndMapping(node, classSMAP.intervals[index])
41+
}
3942
}
4043

4144
class LabelAndMapping(val lineNumberNode: LineNumberNode, val mapper: RangeMapping)

compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
2626
import org.jetbrains.kotlin.codegen.context.CodegenContext
2727
import org.jetbrains.kotlin.codegen.context.RootContext
2828
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
29+
import org.jetbrains.kotlin.codegen.inline.InlineCache
2930
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
3031
import org.jetbrains.kotlin.codegen.optimization.OptimizationClassBuilderFactory
3132
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -89,6 +90,7 @@ class GenerationState @JvmOverloads constructor(
8990
}
9091

9192
val fileClassesProvider: CodegenFileClassesProvider = CodegenFileClassesProvider()
93+
val inlineCache: InlineCache = InlineCache()
9294

9395
private fun getIncrementalCacheForThisTarget() =
9496
if (incrementalCompilationComponents != null && targetId != null)

0 commit comments

Comments
 (0)