[IR][codegen] Moved objc allocs from codegen to InteropLowering

This commit is contained in:
Igor Chevdar
2020-06-02 20:32:07 +05:00
parent 5cb1de5d84
commit 97d3d39187
5 changed files with 73 additions and 89 deletions
@@ -386,7 +386,7 @@ internal fun KotlinStubs.generateObjCCall(
+result
}
private fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
internal fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
val classDescriptor = symbol.descriptor
assert(!classDescriptor.isObjCMetaClass())
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(symbol.typeWithStarProjections))
@@ -144,22 +144,12 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
if (!function.isTypedIntrinsic) {
return null
}
val intrinsicType = getIntrinsicType(callSite)
return when (intrinsicType) {
return when (getIntrinsicType(callSite)) {
IntrinsicType.IMMUTABLE_BLOB -> {
@Suppress("UNCHECKED_CAST")
val arg = callSite.getValueArgument(0) as IrConst<String>
context.llvm.staticData.createImmutableBlob(arg)
}
IntrinsicType.OBJC_INIT_BY -> {
val receiver = environment.evaluateExpression(callSite.extensionReceiver!!)
val irConstructorCall = callSite.getValueArgument(0) as IrConstructorCall
val constructorDescriptor = irConstructorCall.symbol.owner
val constructorArgs = environment.evaluateExplicitArgs(irConstructorCall)
val args = listOf(receiver) + constructorArgs
environment.evaluateCall(constructorDescriptor, args, Lifetime.IRRELEVANT)
receiver
}
IntrinsicType.OBJC_GET_SELECTOR -> {
val selector = (callSite.getValueArgument(0) as IrConst<*>).value as String
environment.functionGenerationContext.genObjCSelector(selector)
@@ -2142,21 +2142,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
lifetime = resultLifetime(callee), exceptionHandler = currentCodeContext.exceptionHandler)
}
// TODO: OBJC-CONSTRUCTOR-CALL. Move to InteropLowering.
constructedClass.isObjCClass() -> {
assert(constructedClass.isKotlinObjCClass()) // Calls to other ObjC class constructors must be lowered.
val symbols = context.ir.symbols
val rawPtr = callDirect(
symbols.interopAllocObjCObject.owner,
listOf(genGetObjCClass(constructedClass)),
Lifetime.IRRELEVANT
)
callDirect(symbols.interopInterpretObjCPointer.owner, listOf(rawPtr), resultLifetime(callee)).also {
// Balance pointer retained by alloc:
callDirect(symbols.interopObjCRelease.owner, listOf(rawPtr), Lifetime.IRRELEVANT)
}
}
constructedClass.isObjCClass() -> error("Call should've been lowered: ${callee.dump()}")
else -> functionGenerationContext.allocInstance(constructedClass, resultLifetime(callee))
}
@@ -672,18 +672,21 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
expression.transformChildrenVoid()
val callee = expression.symbol.owner
val initMethod = callee.getObjCInitMethod() ?: return expression
val initMethod = callee.getObjCInitMethod()
if (initMethod != null) {
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
assert(expression.extensionReceiver == null)
assert(expression.dispatchReceiver == null)
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
assert(expression.extensionReceiver == null)
assert(expression.dispatchReceiver == null)
val constructedClass = callee.constructedClass
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
return builder.at(expression).run {
val classPtr = getObjCClass(constructedClass.symbol)
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
val constructedClass = callee.constructedClass
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
return builder.at(expression).run {
val classPtr = getObjCClass(constructedClass.symbol)
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
}
}
return expression
}
private fun IrBuilderWithScope.ensureObjCReferenceNotNull(expression: IrExpression): IrExpression =
@@ -896,13 +899,35 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
expression.transformChildrenVoid(this)
val function = expression.symbol.owner
val inlinedClass = function.returnType.getInlinedClassNative()
val callee = expression.symbol.owner
val inlinedClass = callee.returnType.getInlinedClassNative()
if (inlinedClass?.descriptor == interop.cPointer || inlinedClass?.descriptor == interop.nativePointed) {
context.reportCompilationError("Native interop types constructors must not be called directly",
irFile, expression)
}
return expression
val constructedClass = callee.constructedClass
if (!constructedClass.isObjCClass())
return expression
assert(constructedClass.isKotlinObjCClass()) // Calls to other ObjC class constructors must be lowered.
return builder.at(expression).irBlock {
val rawPtr = irTemporary(irCall(symbols.interopAllocObjCObject.owner).apply {
putValueArgument(0, getObjCClass(symbols, constructedClass.symbol))
})
val instance = irTemporary(irCall(symbols.interopInterpretObjCPointer.owner).apply {
putValueArgument(0, irGet(rawPtr))
})
// Balance pointer retained by alloc:
+irCall(symbols.interopObjCRelease.owner).apply {
putValueArgument(0, irGet(rawPtr))
}
+irCall(symbols.initInstance).apply {
putValueArgument(0, irGet(instance))
putValueArgument(1, expression)
}
+irGet(instance)
}
}
/**
@@ -933,6 +958,38 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
}
override fun visitCall(expression: IrCall): IrExpression {
val intrinsicType = tryGetIntrinsicType(expression)
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
// Need to do this separately as otherwise [expression.transformChildrenVoid(this)] would be called
// and the [IrConstructorCall] would be transformed which is not what we want.
val intrinsic = interop.objCObjectInitBy.name
val argument = expression.getValueArgument(0)!!
val constructorCall = argument as? IrConstructorCall
?: context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
irFile, argument)
val constructedClass = constructorCall.symbol.owner.constructedClass
val extensionReceiver = expression.extensionReceiver!!
if (extensionReceiver !is IrGetValue ||
!extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) {
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
irFile, extensionReceiver)
}
constructorCall.transformChildrenVoid(this)
return builder.at(expression).irBlock {
val instance = extensionReceiver.symbol.owner
+irCall(symbols.initInstance).apply {
putValueArgument(0, irGet(instance))
putValueArgument(1, constructorCall)
}
+irGet(instance)
}
}
expression.transformChildrenVoid(this)
builder.at(expression)
@@ -957,8 +1014,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
tryGenerateInteropConstantRead(expression)?.let { return it }
val intrinsicType = tryGetIntrinsicType(expression)
if (intrinsicType != null) {
return when (intrinsicType) {
IntrinsicType.INTEROP_BITS_TO_FLOAT -> {
@@ -1097,26 +1152,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
IntrinsicType.INTEROP_MEMORY_COPY -> {
TODO("So far unsupported")
}
IntrinsicType.OBJC_INIT_BY -> {
val intrinsic = interop.objCObjectInitBy.name
val argument = expression.getValueArgument(0)!!
val constructedClass = (argument as? IrConstructorCall)?.symbol?.owner?.constructedClass
if (constructedClass == null) {
context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
irFile, argument)
}
val extensionReceiver = expression.extensionReceiver!!
if (extensionReceiver !is IrGetValue ||
!extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) {
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
irFile, extensionReceiver)
}
expression
}
IntrinsicType.WORKER_EXECUTE -> {
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(2)!!)
@@ -10,8 +10,6 @@ import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.backend.konan.DirectedGraph
import org.jetbrains.kotlin.backend.konan.DirectedGraphNode
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.isObjCClass
import org.jetbrains.kotlin.ir.util.parentAsClass
internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol)
: DirectedGraphNode<DataFlowIR.FunctionSymbol> {
@@ -111,31 +109,6 @@ internal class CallGraphBuilder(val context: Context,
private inline fun DataFlowIR.FunctionBody.forEachCallSite(block: (DataFlowIR.Node.Call) -> Unit) =
nodes.forEach { node ->
when (node) {
// TODO: OBJC-CONSTRUCTOR-CALL
is DataFlowIR.Node.NewObject -> {
block(node)
if (node.irCallSite?.symbol?.owner?.parentAsClass?.isObjCClass() == true) {
block(DataFlowIR.Node.Call(
callee = moduleDFG.symbolTable.mapFunction(symbols.interopAllocObjCObject.owner),
arguments = listOf(DataFlowIR.Edge(DataFlowIR.Node.Null, null)),
returnType = moduleDFG.symbolTable.mapType(symbols.interopAllocObjCObject.owner.returnType),
irCallSite = null)
)
block(DataFlowIR.Node.Call(
callee = moduleDFG.symbolTable.mapFunction(symbols.interopInterpretObjCPointer.owner),
arguments = listOf(DataFlowIR.Edge(DataFlowIR.Node.Null, null)),
returnType = moduleDFG.symbolTable.mapType(symbols.interopInterpretObjCPointer.owner.returnType),
irCallSite = null)
)
block(DataFlowIR.Node.Call(
callee = moduleDFG.symbolTable.mapFunction(symbols.interopObjCRelease.owner),
arguments = listOf(DataFlowIR.Edge(DataFlowIR.Node.Null, null)),
returnType = moduleDFG.symbolTable.mapType(symbols.interopObjCRelease.owner.returnType),
irCallSite = null)
)
}
}
is DataFlowIR.Node.Call -> block(node)
is DataFlowIR.Node.Singleton ->