[K/N] Rework is checks and as casts codegeneration

^KT-58707
^KT-59022
This commit is contained in:
Pavel Kunyavskiy
2023-06-01 10:06:01 +02:00
committed by Space Team
parent b2212b9275
commit f2520a9cb7
21 changed files with 316 additions and 90 deletions
@@ -1185,7 +1185,7 @@ internal class CodeGeneratorVisitor(
genCatchBlock()
return // Remaining catch clauses are unreachable.
} else {
val isInstance = genInstanceOf(exception, catch.catchParameter.type.getClass()!!)
val isInstance = genInstanceOfImpl(exception, catch.catchParameter.type.getClass()!!)
val body = functionGenerationContext.basicBlock("catch", catch.startLocation)
val nextCheck = functionGenerationContext.basicBlock("catchCheck", catch.endLocation)
functionGenerationContext.condBr(isInstance, body, nextCheck)
@@ -1519,73 +1519,123 @@ internal class CodeGeneratorVisitor(
val dstClass = value.typeOperand.getClass()
?: error("No class for ${value.typeOperand.render()} from \n${functionGenerationContext.irFunction?.render()}")
val srcArg = evaluateExpression(value.argument, resultSlot)
assert(srcArg.type == codegen.kObjHeaderPtr)
with(functionGenerationContext) {
ifThen(not(genInstanceOf(srcArg, dstClass))) {
if (dstClass.defaultType.isObjCObjectType()) {
val dstFullClassName = dstClass.fqNameWhenAvailable?.toString() ?: dstClass.name.toString()
callDirect(
context.ir.symbols.throwTypeCastException.owner,
listOf(srcArg, codegen.staticData.kotlinStringLiteral(dstFullClassName).llvm),
Lifetime.GLOBAL,
null
)
} else {
val dstTypeInfo = functionGenerationContext.bitcast(llvm.int8PtrType, codegen.typeInfoValue(dstClass))
callDirect(
context.ir.symbols.throwClassCastException.owner,
listOf(srcArg, dstTypeInfo),
Lifetime.GLOBAL,
null
)
return genInstanceOf(
value,
dstClass,
resultSlot,
onSuperClassCast = {
it.takeIf { value.typeOperand.isNullable() }
},
onNull = {
if (value.typeOperand.isNullable()) {
codegen.kNullObjHeaderPtr
} else {
callDirect(
context.ir.symbols.throwNullPointerException.owner,
listOf(),
Lifetime.GLOBAL,
null
)
}
},
onCheck = { argument, checkResult ->
with(functionGenerationContext) {
if (checkResult != kTrue) {
ifThen(not(checkResult)) {
if (dstClass.defaultType.isObjCObjectType()) {
val dstFullClassName = dstClass.fqNameWhenAvailable?.toString() ?: dstClass.name.toString()
callDirect(
context.ir.symbols.throwTypeCastException.owner,
listOf(argument, codegen.staticData.kotlinStringLiteral(dstFullClassName).llvm),
Lifetime.GLOBAL,
null
)
} else {
val dstTypeInfo = functionGenerationContext.bitcast(llvm.int8PtrType, codegen.typeInfoValue(dstClass))
callDirect(
context.ir.symbols.throwClassCastException.owner,
listOf(argument, dstTypeInfo),
Lifetime.GLOBAL,
null
)
}
}
}
argument
}
}
}
}
return srcArg
)
}
//-------------------------------------------------------------------------//
private fun evaluateInstanceOf(value: IrTypeOperatorCall): LLVMValueRef {
context.log{"evaluateInstanceOf : ${ir2string(value)}"}
val type = value.typeOperand
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
val bbExit = functionGenerationContext.basicBlock("instance_of_exit", value.startLocation)
val bbInstanceOf = functionGenerationContext.basicBlock("instance_of_notnull", value.startLocation)
val bbNull = functionGenerationContext.basicBlock("instance_of_null", value.startLocation)
val condition = functionGenerationContext.icmpEq(srcArg, codegen.kNullObjHeaderPtr)
functionGenerationContext.condBr(condition, bbNull, bbInstanceOf)
functionGenerationContext.positionAtEnd(bbNull)
val resultNull = if (type.isNullable()) kTrue else kFalse
functionGenerationContext.br(bbExit)
functionGenerationContext.positionAtEnd(bbInstanceOf)
val typeOperandClass = value.typeOperand.getClass()
val resultInstanceOf = if (typeOperandClass != null) {
genInstanceOf(srcArg, typeOperandClass)
} else {
// E.g. when generating type operation with reified type parameter in the original body of inline function.
kTrue
// TODO: this code should be unreachable, recheck.
}
functionGenerationContext.br(bbExit)
val bbInstanceOfResult = functionGenerationContext.currentBlock
functionGenerationContext.positionAtEnd(bbExit)
val result = functionGenerationContext.phi(llvm.int1Type)
functionGenerationContext.addPhiIncoming(result, bbNull to resultNull, bbInstanceOfResult to resultInstanceOf)
return result
return genInstanceOf(
value,
type.getClass() ?: context.ir.symbols.any.owner,
resultSlot = null,
onSuperClassCast = { arg ->
if (type.isNullable())
kTrue
else
functionGenerationContext.icmpNe(arg, codegen.kNullObjHeaderPtr)
},
onNull = { if (type.isNullable()) kTrue else kFalse },
onCheck = { _, checkResult -> checkResult }
)
}
//-------------------------------------------------------------------------//
private fun genInstanceOf(obj: LLVMValueRef, dstClass: IrClass) = with(functionGenerationContext) {
private inline fun genInstanceOf(
value: IrTypeOperatorCall,
dstClass: IrClass,
resultSlot: LLVMValueRef?,
onSuperClassCast: (LLVMValueRef) -> LLVMValueRef?,
onNull: () -> LLVMValueRef,
onCheck: (argument: LLVMValueRef, checkResult: LLVMValueRef) -> LLVMValueRef,
) : LLVMValueRef {
val srcArg = evaluateExpression(value.argument, resultSlot)
require(srcArg.type == codegen.kObjHeaderPtr)
val isSuperClassCast = value.argument.type.isSubtypeOfClass(dstClass.symbol)
if (isSuperClassCast) {
onSuperClassCast(srcArg)?.let { return it }
}
return with(functionGenerationContext) {
val bbInstanceOf = basicBlock("instance_of_notnull", value.startLocation)
val bbNull = basicBlock("instance_of_null", value.startLocation)
val condition = icmpEq(srcArg, codegen.kNullObjHeaderPtr)
condBr(condition, bbNull, bbInstanceOf)
positionAtEnd(bbNull)
val resultNull = onNull()
val resultNullBB = currentBlock.takeIf { !isAfterTerminator() }
positionAtEnd(bbInstanceOf)
val resultInstanceOf = onCheck(srcArg, if (isSuperClassCast) kTrue else genInstanceOfImpl(srcArg, dstClass))
val resultInstanceOfBB = currentBlock.also { require(!isAfterTerminator()) }
if (resultNullBB == null) {
resultInstanceOf
} else {
val bbExit = basicBlock("instance_of_exit", value.startLocation)
positionAtEnd(bbExit)
appendingTo(resultInstanceOfBB) { br(bbExit) }
appendingTo(resultNullBB) { br(bbExit) }
val result = phi(value.type.toLLVMType(llvm))
addPhiIncoming(result, resultNullBB to resultNull, resultInstanceOfBB to resultInstanceOf)
result
}
}
}
private fun genInstanceOfImpl(obj: LLVMValueRef, dstClass: IrClass) = with(functionGenerationContext) {
if (dstClass.defaultType.isObjCObjectType()) {
genInstanceOfObjC(obj, dstClass)
} else with(VirtualTablesLookup) {
@@ -272,7 +272,9 @@ private fun IrBlockBodyBuilder.buildTypeSafeBarrier(function: IrFunction,
// But let's keep it simple here for now; JVM backend doesn't do this anyway.
if (!type.isNullableAny()) {
+returnIfBadType(irGet(valueParameters[i]), type,
// Here, we can't trust value parameter type until we check it, because of @UnsafeVariance
// So we add implicit cast to avoid type check optimization
+returnIfBadType(irImplicitCast(irGet(valueParameters[i]), context.irBuiltIns.anyNType), type,
if (typeSafeBarrierDescription == SpecialGenericSignatures.TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT)
irGet(valueParameters[2])
else irConst(typeSafeBarrierDescription.defaultValue)
@@ -55,37 +55,10 @@ internal class TypeOperatorLowering(val context: CommonBackendContext) : FileLow
private fun lowerCast(expression: IrTypeOperatorCall): IrExpression {
builder.at(expression)
val typeOperand = expression.typeOperand.erasure()
// assert (!TypeUtils.hasNullableSuperType(typeOperand)) // So that `isNullable()` <=> `isMarkedNullable`.
// TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts.
return when {
expression.argument.type.isSubtypeOf(typeOperand, context.typeSystem) -> expression.argument
expression.argument.type.isNullable() -> {
with(builder) {
irLetS(expression.argument) { argument ->
irIfThenElse(
type = expression.type,
condition = irEqeqeq(irGet(argument.owner), irNull()),
thenPart = if (typeOperand.isNullable())
irNull()
else
irCall(this@TypeOperatorLowering.context.ir.symbols.throwNullPointerException.owner),
elsePart = irAs(irGet(argument.owner), typeOperand.makeNotNull())
)
}
}
}
typeOperand.isMarkedNullable() -> builder.irAs(expression.argument, typeOperand.makeNotNull())
typeOperand == expression.typeOperand -> expression
else -> builder.irAs(expression.argument, typeOperand)
return if (typeOperand == expression.typeOperand) {
expression
} else {
builder.irAs(expression.argument, typeOperand)
}
}