Support generic array creation for reified inlined

This commit is contained in:
Denis Zharkov
2014-10-02 15:10:39 +04:00
committed by Andrey Breslav
parent 060c30746f
commit 4a66ab3627
9 changed files with 281 additions and 9 deletions
@@ -30,9 +30,7 @@ import org.jetbrains.jet.backend.common.CodegenUtil;
import org.jetbrains.jet.codegen.binding.CalculatedClosure;
import org.jetbrains.jet.codegen.binding.CodegenBinding;
import org.jetbrains.jet.codegen.context.*;
import org.jetbrains.jet.codegen.inline.InlineCodegen;
import org.jetbrains.jet.codegen.inline.InlineCodegenUtil;
import org.jetbrains.jet.codegen.inline.NameGenerator;
import org.jetbrains.jet.codegen.inline.*;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.jet.codegen.state.GenerationState;
@@ -2186,8 +2184,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
resolvedCall = ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
}
CallGenerator callGenerator = getOrCreateCallGenerator(resolvedCall);
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
CallGenerator callGenerator = getOrCreateCallGenerator(descriptor, resolvedCall.getCall().getCallElement());
assert callGenerator == defaultCallGenerator || !tailRecursionCodegen.isTailRecursion(resolvedCall) :
"Tail recursive method can't be inlined: " + descriptor;
@@ -2230,7 +2228,11 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
@NotNull
protected CallGenerator getOrCreateCallGenerator(@NotNull CallableDescriptor descriptor, @Nullable JetElement callElement) {
protected CallGenerator getOrCreateCallGenerator(
@NotNull CallableDescriptor descriptor,
@Nullable JetElement callElement,
@Nullable ReifiedTypeParameterMappings reifierTypeParameterMappings
) {
if (callElement == null) return defaultCallGenerator;
boolean isInline = state.isInlineEnabled() &&
@@ -2240,7 +2242,43 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (!isInline) return defaultCallGenerator;
SimpleFunctionDescriptor original = DescriptorUtils.unwrapFakeOverride((SimpleFunctionDescriptor) descriptor.getOriginal());
return new InlineCodegen(this, state, original, callElement);
return new InlineCodegen(this, state, original, callElement, reifierTypeParameterMappings);
}
@NotNull
public CallGenerator getOrCreateCallGenerator(@NotNull FunctionDescriptor descriptor, @Nullable JetNamedFunction function) {
return getOrCreateCallGenerator(descriptor, function, null);
}
@NotNull
private CallGenerator getOrCreateCallGenerator(@NotNull ResolvedCall<?> resolvedCall) {
Map<TypeParameterDescriptor, JetType> typeArguments = resolvedCall.getTypeArguments();
ReifiedTypeParameterMappings mappings = new ReifiedTypeParameterMappings(typeArguments.size());
for (Map.Entry<TypeParameterDescriptor, JetType> entry : typeArguments.entrySet()) {
TypeParameterDescriptor key = entry.getKey();
if (!key.isReified()) continue;
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(entry.getValue());
if (parameterDescriptor == null) {
// type is not generic
// boxType call needed because inlined method is compiled for T as java/lang/Object
mappings.addParameterMappingToType(
key.getIndex(),
key.getName().getIdentifier(),
boxType(asmType(entry.getValue()))
);
}
else {
mappings.addParameterMappingToNewParameter(
key.getIndex(),
key.getName().getIdentifier(),
parameterDescriptor.getIndex()
);
}
}
return getOrCreateCallGenerator(
resolvedCall.getResultingDescriptor(), resolvedCall.getCall().getCallElement(), mappings
);
}
public void generateReceiverValue(@NotNull ReceiverValue receiverValue, @NotNull Type type) {
@@ -3373,6 +3411,16 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (isArray) {
gen(args.get(0), Type.INT_TYPE);
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(
arrayType.getArguments().get(0).getType()
);
if (parameterDescriptor != null && parameterDescriptor.isReified()) {
v.iconst(parameterDescriptor.getIndex());
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false
);
}
v.newarray(boxType(asmType(arrayType.getArguments().get(0).getType())));
}
else {
@@ -76,13 +76,16 @@ public class InlineCodegen implements CallGenerator {
protected final ParametersBuilder invocationParamBuilder = ParametersBuilder.newBuilder();
protected final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
private final ReifiedTypeInliner reifiedTypeInliner;
private LambdaInfo activeLambda;
public InlineCodegen(
@NotNull ExpressionCodegen codegen,
@NotNull GenerationState state,
@NotNull SimpleFunctionDescriptor functionDescriptor,
@NotNull JetElement callElement
@NotNull JetElement callElement,
@Nullable ReifiedTypeParameterMappings typeParameterMappings
) {
assert functionDescriptor.getInlineStrategy().isInline() : "InlineCodegen could inline only inline function but " + functionDescriptor;
@@ -91,6 +94,9 @@ public class InlineCodegen implements CallGenerator {
this.codegen = codegen;
this.callElement = callElement;
this.functionDescriptor = functionDescriptor.getOriginal();
reifiedTypeInliner = new ReifiedTypeInliner(typeParameterMappings);
initialFrameSize = codegen.getFrameMap().getCurrentSize();
context = (MethodContext) getContext(functionDescriptor, state);
@@ -200,6 +206,7 @@ public class InlineCodegen implements CallGenerator {
}
private InlineResult inlineCall(MethodNode node) {
reifiedTypeInliner.reifyInstructions(node.instructions);
generateClosuresBodies();
//through generation captured parameters will be added to invocationParamBuilder
@@ -0,0 +1,125 @@
/*
* Copyright 2010-2014 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.jet.codegen.inline
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.IntInsnNode
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods
import kotlin.platform.platformStatic
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
import java.util.ArrayList
public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParameterMappings?) {
class object {
public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray"
}
public fun reifyInstructions(instructions: InsnList) {
if (parametersMapping == null) return
for (insn in instructions.toArray()) {
if (isReifiedMarker(insn)) {
processReifyMarker(insn as MethodInsnNode, instructions)
}
}
}
private fun isReifiedMarker(insn: AbstractInsnNode): Boolean {
if (insn.getOpcode() != Opcodes.INVOKESTATIC || insn !is MethodInsnNode) return false
return insn.owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && insn.name.startsWith("reify")
}
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList) {
val mapping = getTypeParameterMapping(insn) ?: return
if (mapping.asmType != null) {
if (!when (insn.name) {
NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, mapping.asmType)
else -> false
}) {
return
}
instructions.remove(insn.getPrevious()!!)
instructions.remove(insn)
} else {
instructions.set(insn.getPrevious()!!, iconstInsn(mapping.newIndex!!))
}
}
private fun processNewArray(insn: MethodInsnNode, parameter: Type): Boolean {
if (insn.getNext()?.getOpcode() != Opcodes.ANEWARRAY) return false
val next = insn.getNext() as TypeInsnNode
next.desc = parameter.getInternalName()
return true
}
private fun getParameterIndex(insn: MethodInsnNode): Int? {
val prev = insn.getPrevious()!!
return when (prev.getOpcode()) {
Opcodes.ICONST_0 -> 0
Opcodes.ICONST_1 -> 1
Opcodes.ICONST_2 -> 2
Opcodes.ICONST_3 -> 3
Opcodes.ICONST_4 -> 4
Opcodes.ICONST_5 -> 5
Opcodes.BIPUSH, Opcodes.SIPUSH -> (prev as IntInsnNode).operand
Opcodes.LDC -> (prev as LdcInsnNode).cst as Int
else -> throw AssertionError("Unexpected opcode ${prev.getOpcode()}")
}
}
private fun getTypeParameterMapping(insn: MethodInsnNode): ReifiedTypeParameterMapping? {
return parametersMapping?.get(getParameterIndex(insn) ?: return null)
}
}
public class ReifiedTypeParameterMappings(private val size: Int) {
private val mappingsByIndex = arrayOfNulls<ReifiedTypeParameterMapping>(size)
private val indexByParameterName = hashMapOf<String, Int>()
public fun addParameterMappingToType(index: Int, name: String, asmType: Type) {
mappingsByIndex[index] = ReifiedTypeParameterMapping(name, asmType, null)
indexByParameterName[name] = index
}
public fun addParameterMappingToNewParameter(index: Int, name: String, newIndex: Int) {
mappingsByIndex[index] = ReifiedTypeParameterMapping(name, null, newIndex)
indexByParameterName[name] = index
}
fun get(index: Int) = mappingsByIndex[index]
fun get(name: String): ReifiedTypeParameterMapping? {
return this[indexByParameterName[name] ?: return null]
}
}
public class ReifiedTypeParameterMapping(val name: String, val asmType: Type?, val newIndex: Int?)
private fun iconstInsn(n: Int): AbstractInsnNode {
val node = MethodNode()
InstructionAdapter(node).iconst(n)
return node.instructions.getFirst()!!
}
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve.calls;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.JetExpression;
@@ -38,7 +39,10 @@ public class TypeParameterAsReifiedCheck implements CallResolverExtension {
for (Map.Entry<TypeParameterDescriptor, JetType> entry : typeArguments.entrySet()) {
TypeParameterDescriptor parameter = entry.getKey();
JetType argument = entry.getValue();
if (parameter.isReified() && argument.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
ClassifierDescriptor argumentDeclarationDescription = argument.getConstructor().getDeclarationDescriptor();
if (parameter.isReified() && argumentDeclarationDescription instanceof TypeParameterDescriptor &&
!((TypeParameterDescriptor) argumentDeclarationDescription).isReified()
) {
JetExpression callee = context.call.getCalleeExpression();
PsiElement element = callee != null ? callee : context.call.getCallElement();
context.trace.report(Errors.TYPE_PARAMETER_AS_REIFIED.on(element, typeArguments.keySet().iterator().next()));
@@ -0,0 +1,13 @@
import kotlin.InlineOption.*
inline fun<reified T> createArray(n: Int, inlineOptions(ONLY_LOCAL_RETURN) block: () -> T): Array<T> {
return Array<T>(n) { block() }
}
fun box(): String {
val x = createArray<Int>(5) { 3 }
assert(x.all { it == 3 })
return "OK"
}
@@ -0,0 +1,18 @@
import kotlin.InlineOption.*
inline fun<reified T> createArray(n: Int, inlineOptions(ONLY_LOCAL_RETURN) block: () -> T): Array<T> {
return Array<T>(n) { block() }
}
inline fun<T1, T2, T3, T4, T5, T6, reified R> recursive(
inlineOptions(ONLY_LOCAL_RETURN) block: () -> R
): Array<R> {
return createArray(5) { block() }
}
fun box(): String {
val x = recursive<Int, Int, Int, Int, Int, Int, String>(){ "abc" }
assert(x.all { it == "abc" })
return "OK"
}
@@ -0,0 +1,22 @@
import kotlin.InlineOption.*
inline fun<reified T1, reified T2> createArray(n: Int, inlineOptions(ONLY_LOCAL_RETURN) block: () -> Pair<T1, T2>): Pair<Array<T1>, Array<T2>> {
return Pair(Array(n) { block().first }, Array(n) { block().second })
}
inline fun<T1, T2, T3, T4, T5, T6, reified R> recursive(
inlineOptions(ONLY_LOCAL_RETURN) block: () -> R
): Pair<Array<R>, Array<R>> {
return createArray(5) { Pair(block(), block()) }
}
fun box(): String {
val y = createArray(5) { Pair(1, "test") }
val x = recursive<Int, Int, Int, Int, Int, Int, String>(){ "abc" }
assert(y.first.all { it == 1 } )
assert(y.second.all { it == "test" })
assert(x.first.all { it == "abc" })
assert(x.second.all { it == "abc" })
return "OK"
}
@@ -30,7 +30,7 @@ import java.util.regex.Pattern;
@SuppressWarnings("all")
@TestMetadata("compiler/testData/codegen/boxWithStdlib")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.DefaultArguments.class, BlackBoxWithStdlibCodegenTestGenerated.Enum.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.Intrinsics.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.NonLocalReturns.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformStatic.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformTypes.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.StoreStackBeforeInline.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, BlackBoxWithStdlibCodegenTestGenerated.WhenEnumOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.WhenStringOptimization.class})
@InnerTestClasses({BlackBoxWithStdlibCodegenTestGenerated.Annotations.class, BlackBoxWithStdlibCodegenTestGenerated.Arrays.class, BlackBoxWithStdlibCodegenTestGenerated.BoxingOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.CallableReference.class, BlackBoxWithStdlibCodegenTestGenerated.Casts.class, BlackBoxWithStdlibCodegenTestGenerated.DataClasses.class, BlackBoxWithStdlibCodegenTestGenerated.DefaultArguments.class, BlackBoxWithStdlibCodegenTestGenerated.Enum.class, BlackBoxWithStdlibCodegenTestGenerated.Evaluate.class, BlackBoxWithStdlibCodegenTestGenerated.FullJdk.class, BlackBoxWithStdlibCodegenTestGenerated.HashPMap.class, BlackBoxWithStdlibCodegenTestGenerated.Intrinsics.class, BlackBoxWithStdlibCodegenTestGenerated.JdkAnnotations.class, BlackBoxWithStdlibCodegenTestGenerated.NonLocalReturns.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformNames.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformStatic.class, BlackBoxWithStdlibCodegenTestGenerated.PlatformTypes.class, BlackBoxWithStdlibCodegenTestGenerated.Ranges.class, BlackBoxWithStdlibCodegenTestGenerated.Reflection.class, BlackBoxWithStdlibCodegenTestGenerated.Regressions.class, BlackBoxWithStdlibCodegenTestGenerated.Reified.class, BlackBoxWithStdlibCodegenTestGenerated.StoreStackBeforeInline.class, BlackBoxWithStdlibCodegenTestGenerated.Strings.class, BlackBoxWithStdlibCodegenTestGenerated.ToArray.class, BlackBoxWithStdlibCodegenTestGenerated.Vararg.class, BlackBoxWithStdlibCodegenTestGenerated.When.class, BlackBoxWithStdlibCodegenTestGenerated.WhenEnumOptimization.class, BlackBoxWithStdlibCodegenTestGenerated.WhenStringOptimization.class})
@RunWith(JUnit3RunnerWithInners.class)
public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInBoxWithStdlib() throws Exception {
@@ -2412,6 +2412,33 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/reified")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Reified extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInReified() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reified"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("newArrayInt.kt")
public void testNewArrayInt() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/newArrayInt.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("recursiveNewArray.kt")
public void testRecursiveNewArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/recursiveNewArray.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("sameIndexRecursive.kt")
public void testSameIndexRecursive() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/sameIndexRecursive.kt");
doTestWithStdlib(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxWithStdlib/storeStackBeforeInline")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -102,6 +102,14 @@ public class Intrinsics {
"throwParameterIsNullException"
));
private static void throwUndefinedForReified() {
throw new UnsupportedOperationException("You should not use functions with reified parameter without inline");
}
public static void reifyNewArray(int parameterTypeIndex) {
throwUndefinedForReified();
}
public static <T extends Throwable> T sanitizeStackTrace(T throwable) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>(stackTrace.length);