Unify code generation of as/is operation

They should be the same for common and reified cases
This commit is contained in:
Denis Zharkov
2015-12-28 10:53:02 +03:00
parent da53d8cbf4
commit d511059cfa
5 changed files with 68 additions and 134 deletions
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethod;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter; import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicPropertyGetter;
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt; import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
@@ -3674,30 +3673,17 @@ The "returned" value of try expression with no finally is either the last expres
} }
boolean safeAs = opToken == KtTokens.AS_SAFE; boolean safeAs = opToken == KtTokens.AS_SAFE;
Type type = boxType(asmType(rightType));
if (TypeUtils.isReifiedTypeParameter(rightType)) { if (TypeUtils.isReifiedTypeParameter(rightType)) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType, putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
: ReifiedTypeInliner.OperationKind.AS); : ReifiedTypeInliner.OperationKind.AS);
v.checkcast(boxType(asmType(rightType))); v.checkcast(type);
return Unit.INSTANCE; return Unit.INSTANCE;
} }
if (opToken != KtTokens.AS_SAFE) { CodegenUtilKt.generateAsCast(v, rightType, type, safeAs);
if (!TypeUtils.isNullableType(rightType)) {
CodegenUtilKt.generateNullCheckForNonSafeAs(v, rightType);
}
}
else {
v.dup();
generateInstanceOfInstruction(rightType);
Label ok = new Label();
v.ifne(ok);
v.pop();
v.aconst(null);
v.mark(ok);
}
generateCheckCastInstruction(rightType, safeAs);
return Unit.INSTANCE; return Unit.INSTANCE;
} }
}); });
@@ -3749,38 +3735,19 @@ The "returned" value of try expression with no finally is either the last expres
v.dup(); v.dup();
} }
Type type = boxType(asmType(kotlinType));
if (TypeUtils.isReifiedTypeParameter(kotlinType)) { if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS); putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
v.instanceOf(boxType(asmType(kotlinType))); v.instanceOf(type);
return null; return null;
} }
CodegenUtilKt.generateIsCheck(v, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() { CodegenUtilKt.generateIsCheck(v, kotlinType, type);
@Override
public Unit invoke(InstructionAdapter adapter) {
generateInstanceOfInstruction(kotlinType);
return Unit.INSTANCE;
}
});
return null; return null;
} }
}); });
} }
private void generateInstanceOfInstruction(@NotNull KotlinType kotlinType) {
Type type = boxType(asmType(kotlinType));
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
TypeIntrinsics.instanceOf(v, kotlinType, type);
}
@NotNull
private StackValue generateCheckCastInstruction(@NotNull KotlinType kotlinType, boolean safeAs) {
Type type = boxType(asmType(kotlinType));
assert !TypeUtils.isReifiedTypeParameter(kotlinType) : "This method should not be called with type based on reified type parameter, but found " + kotlinType;
TypeIntrinsics.checkcast(v, kotlinType, type, safeAs);
return StackValue.onStack(type);
}
public void putReifiedOperationMarkerIfTypeIsReifiedParameter( public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind @NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
) { ) {
@@ -886,13 +886,7 @@ public class FunctionCodegen {
iv.ifnonnull(afterBarrier); iv.ifnonnull(afterBarrier);
} }
else { else {
CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() { CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType));
@Override
public Unit invoke(InstructionAdapter adapter) {
TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType));
return Unit.INSTANCE;
}
});
iv.ifne(afterBarrier); iv.ifne(afterBarrier);
} }
@@ -18,6 +18,7 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
@@ -28,15 +29,17 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
fun generateIsCheck( fun generateIsCheck(
v: InstructionAdapter, v: InstructionAdapter,
isNullable: Boolean, kotlinType: KotlinType,
generateInstanceOfInstruction: (InstructionAdapter) -> Unit asmType: Type
) { ) {
if (isNullable) { if (TypeUtils.isNullableType(kotlinType)) {
val nope = Label() val nope = Label()
val end = Label() val end = Label()
@@ -45,7 +48,8 @@ fun generateIsCheck(
ifnull(nope) ifnull(nope)
generateInstanceOfInstruction(this) TypeIntrinsics.instanceOf(this, kotlinType, asmType)
goTo(end) goTo(end)
mark(nope) mark(nope)
@@ -56,11 +60,37 @@ fun generateIsCheck(
} }
} }
else { else {
generateInstanceOfInstruction(v) TypeIntrinsics.instanceOf(v, kotlinType, asmType)
} }
} }
fun generateNullCheckForNonSafeAs( fun generateAsCast(
v: InstructionAdapter,
kotlinType: KotlinType,
asmType: Type,
isSafe: Boolean
) {
if (!isSafe) {
if (!TypeUtils.isNullableType(kotlinType)) {
generateNullCheckForNonSafeAs(v, kotlinType)
}
}
else {
with(v) {
dup()
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
val ok = Label()
ifne(ok)
pop()
aconst(null)
mark(ok)
}
}
TypeIntrinsics.checkcast(v, kotlinType, asmType, isSafe)
}
private fun generateNullCheckForNonSafeAs(
v: InstructionAdapter, v: InstructionAdapter,
type: KotlinType type: KotlinType
) { ) {
@@ -17,10 +17,9 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.generateAsCast
import org.jetbrains.kotlin.codegen.generateIsCheck import org.jetbrains.kotlin.codegen.generateIsCheck
import org.jetbrains.kotlin.codegen.generateNullCheckForNonSafeAs
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.optimization.common.intConstant import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
@@ -177,64 +176,42 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
private fun processAs(insn: MethodInsnNode, private fun processAs(insn: MethodInsnNode,
instructions: InsnList, instructions: InsnList,
jetType: KotlinType, kotlinType: KotlinType,
asmType: Type, asmType: Type,
safe: Boolean) = safe: Boolean) =
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode -> rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false if (stubCheckcast !is TypeInsnNode) return false
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubCheckcast)
// TODO: refine max stack calculation (it's not always as big as +4)
maxStackSize = Math.max(maxStackSize, 4)
addNullCheckForAsIfNeeded(insn.previous.previous, instructions, jetType, safe)
TypeIntrinsics.checkcast(instanceofInsn, instructions, jetType, asmType, safe)
return true return true
} }
private fun addNullCheckForAsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, jetType: KotlinType, safe: Boolean) { private fun processIs(insn: MethodInsnNode,
if (!safe && !TypeUtils.isNullableType(jetType)) { instructions: InsnList,
val methodNode = MethodNode(InlineCodegenUtil.API) kotlinType: KotlinType,
generateNullCheckForNonSafeAs(InstructionAdapter(methodNode), jetType) asmType: Type) =
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false
InlineCodegenUtil.insertNodeBefore(methodNode, instructions, insn) val newMethodNode = MethodNode(InlineCodegenUtil.API)
maxStackSize = Math.max(maxStackSize, 4) generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
}
}
private fun processIs(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type) = instructions.insert(insn, newMethodNode.instructions)
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { instanceofInsn: AbstractInsnNode -> instructions.remove(stubInstanceOf)
if (instanceofInsn !is TypeInsnNode) return false
addNullCheckForIsIfNeeded(insn, instructions, jetType) // TODO: refine max stack calculation (it's not always as big as +2)
TypeIntrinsics.instanceOf(instanceofInsn, instructions, jetType, asmType) maxStackSize = Math.max(maxStackSize, 2)
return true return true
} }
private fun addNullCheckForIsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, type: KotlinType) {
if (TypeUtils.isNullableType(type)) {
val instanceOf = insn.next
insertNullCheckAround(instructions, insn.previous.previous, instanceOf)
maxStackSize = Math.max(maxStackSize, 2)
}
}
private fun insertNullCheckAround(instructions: InsnList, start: AbstractInsnNode, end: AbstractInsnNode) {
val methodNode = MethodNode(InlineCodegenUtil.API)
var splitIndex: Int = -1
generateIsCheck(InstructionAdapter(methodNode), true) {
splitIndex = methodNode.instructions.size()
}
assert(splitIndex >= 0) {
"Split index should be non-negative, but $splitIndex"
}
val nullCheckInsns = methodNode.instructions.toArray()
nullCheckInsns.take(splitIndex).forEach {
instructions.insertBefore(start, it)
}
nullCheckInsns.drop(splitIndex).reversed().forEach {
instructions.insert(end, it)
}
}
inline private fun rewriteNextTypeInsn( inline private fun rewriteNextTypeInsn(
marker: MethodInsnNode, marker: MethodInsnNode,
expectedNextOpcode: Int, expectedNextOpcode: Int,
@@ -108,40 +108,6 @@ public object TypeIntrinsics {
v.checkcast(asmType) v.checkcast(asmType)
} }
@JvmStatic
public fun checkcast(
checkcastInsn: TypeInsnNode,
instructions: InsnList,
kotlinType: KotlinType, asmType: Type,
safe: Boolean) {
if (safe) {
checkcastInsn.desc = asmType.internalName
return
}
val functionTypeArity = getFunctionTypeArity(kotlinType)
if (functionTypeArity >= 0) {
instructions.insertBefore(checkcastInsn, iconstNode(functionTypeArity))
val beforeCheckcast = typeIntrinsicNode(BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY, BEFORE_CHECKCAST_TO_FUNCTION_OF_ARITY_DESCRIPTOR)
instructions.insertBefore(checkcastInsn, beforeCheckcast)
instructions.insertBefore(checkcastInsn, TypeInsnNode(Opcodes.CHECKCAST, asmType.internalName))
instructions.remove(checkcastInsn)
return
}
val asMutableCollectionMethodName = getAsMutableCollectionMethodName(kotlinType)
if (asMutableCollectionMethodName != null) {
instructions.insertBefore(checkcastInsn,
typeIntrinsicNode(asMutableCollectionMethodName, getAsMutableCollectionDescriptor(asmType)))
instructions.remove(checkcastInsn)
return
}
checkcastInsn.desc = asmType.internalName
}
private val INTRINSICS_CLASS = "kotlin/jvm/internal/TypeIntrinsics" private val INTRINSICS_CLASS = "kotlin/jvm/internal/TypeIntrinsics"
private val IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity" private val IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity"