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.IntrinsicMethods;
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.signature.BothSignatureWriter;
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;
Type type = boxType(asmType(rightType));
if (TypeUtils.isReifiedTypeParameter(rightType)) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(rightType,
safeAs ? ReifiedTypeInliner.OperationKind.SAFE_AS
: ReifiedTypeInliner.OperationKind.AS);
v.checkcast(boxType(asmType(rightType)));
v.checkcast(type);
return Unit.INSTANCE;
}
if (opToken != KtTokens.AS_SAFE) {
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);
}
CodegenUtilKt.generateAsCast(v, rightType, type, safeAs);
generateCheckCastInstruction(rightType, safeAs);
return Unit.INSTANCE;
}
});
@@ -3749,38 +3735,19 @@ The "returned" value of try expression with no finally is either the last expres
v.dup();
}
Type type = boxType(asmType(kotlinType));
if (TypeUtils.isReifiedTypeParameter(kotlinType)) {
putReifiedOperationMarkerIfTypeIsReifiedParameter(kotlinType, ReifiedTypeInliner.OperationKind.IS);
v.instanceOf(boxType(asmType(kotlinType)));
v.instanceOf(type);
return null;
}
CodegenUtilKt.generateIsCheck(v, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter adapter) {
generateInstanceOfInstruction(kotlinType);
return Unit.INSTANCE;
}
});
CodegenUtilKt.generateIsCheck(v, kotlinType, type);
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(
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
) {
@@ -886,13 +886,7 @@ public class FunctionCodegen {
iv.ifnonnull(afterBarrier);
}
else {
CodegenUtilKt.generateIsCheck(iv, kotlinType.isMarkedNullable(), new Function1<InstructionAdapter, Unit>() {
@Override
public Unit invoke(InstructionAdapter adapter) {
TypeIntrinsics.instanceOf(adapter, kotlinType, boxType(delegateParameterType));
return Unit.INSTANCE;
}
});
CodegenUtilKt.generateIsCheck(iv, kotlinType, boxType(delegateParameterType));
iv.ifne(afterBarrier);
}
@@ -18,6 +18,7 @@
package org.jetbrains.kotlin.codegen
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.descriptors.PropertyDescriptor
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.types.ErrorUtils
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.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
fun generateIsCheck(
v: InstructionAdapter,
isNullable: Boolean,
generateInstanceOfInstruction: (InstructionAdapter) -> Unit
kotlinType: KotlinType,
asmType: Type
) {
if (isNullable) {
if (TypeUtils.isNullableType(kotlinType)) {
val nope = Label()
val end = Label()
@@ -45,7 +48,8 @@ fun generateIsCheck(
ifnull(nope)
generateInstanceOfInstruction(this)
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
goTo(end)
mark(nope)
@@ -56,11 +60,37 @@ fun generateIsCheck(
}
}
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,
type: KotlinType
) {
@@ -17,10 +17,9 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.generateAsCast
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.TypeIntrinsics
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
@@ -177,64 +176,42 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
private fun processAs(insn: MethodInsnNode,
instructions: InsnList,
jetType: KotlinType,
kotlinType: KotlinType,
asmType: Type,
safe: Boolean) =
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { instanceofInsn: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
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
}
private fun addNullCheckForAsIfNeeded(insn: AbstractInsnNode, instructions: InsnList, jetType: KotlinType, safe: Boolean) {
if (!safe && !TypeUtils.isNullableType(jetType)) {
val methodNode = MethodNode(InlineCodegenUtil.API)
generateNullCheckForNonSafeAs(InstructionAdapter(methodNode), jetType)
private fun processIs(insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
asmType: Type) =
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false
InlineCodegenUtil.insertNodeBefore(methodNode, instructions, insn)
maxStackSize = Math.max(maxStackSize, 4)
}
}
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
private fun processIs(insn: MethodInsnNode, instructions: InsnList, jetType: KotlinType, asmType: Type) =
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { instanceofInsn: AbstractInsnNode ->
if (instanceofInsn !is TypeInsnNode) return false
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubInstanceOf)
addNullCheckForIsIfNeeded(insn, instructions, jetType)
TypeIntrinsics.instanceOf(instanceofInsn, instructions, jetType, asmType)
// TODO: refine max stack calculation (it's not always as big as +2)
maxStackSize = Math.max(maxStackSize, 2)
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(
marker: MethodInsnNode,
expectedNextOpcode: Int,
@@ -108,40 +108,6 @@ public object TypeIntrinsics {
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 IS_FUNCTON_OF_ARITY_METHOD_NAME = "isFunctionOfArity"