backend.konan: implement callable reference lowering
This commit is contained in:
committed by
SvyatoslavScherbina
parent
8dce1884fd
commit
2fa2727935
+14
@@ -7,6 +7,8 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all implemented interfaces (including those which implemented by a super class)
|
* List of all implemented interfaces (including those which implemented by a super class)
|
||||||
@@ -83,6 +85,18 @@ internal fun KonanBuiltIns.getKonanInternalClass(name: String): ClassDescriptor
|
|||||||
return classifier as ClassDescriptor
|
return classifier as ClassDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val UNBOUND_CALLABLE_REFERENCE = "UnboundCallableReference"
|
||||||
|
|
||||||
|
internal val KonanBuiltIns.unboundCallableReferenceType: KotlinType
|
||||||
|
get() = this.getKonanInternalClass(UNBOUND_CALLABLE_REFERENCE).defaultType
|
||||||
|
|
||||||
|
internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
||||||
|
val classDescriptor = TypeUtils.getClassDescriptor(this) ?: return false
|
||||||
|
|
||||||
|
return !this.isMarkedNullable &&
|
||||||
|
classDescriptor.fqNameUnsafe.asString() == "konan.internal.$UNBOUND_CALLABLE_REFERENCE"
|
||||||
|
}
|
||||||
|
|
||||||
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
||||||
get() {
|
get() {
|
||||||
val constructorReceiver = if (this is ConstructorDescriptor) {
|
val constructorReceiver = if (this is ConstructorDescriptor) {
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the arguments explicitly represented in the IR to the parameters of the accessed function.
|
||||||
|
* The arguments are to be evaluated in the same order as they appear in the resulting list.
|
||||||
|
*/
|
||||||
|
internal fun IrMemberAccessExpression.getArguments(): List<Pair<ParameterDescriptor, IrExpression>> {
|
||||||
|
val res = mutableListOf<Pair<ParameterDescriptor, IrExpression>>()
|
||||||
|
val descriptor = descriptor
|
||||||
|
|
||||||
|
// TODO: ensure the order below corresponds to the one defined in Kotlin specs.
|
||||||
|
|
||||||
|
dispatchReceiver?.let {
|
||||||
|
res += (descriptor.dispatchReceiverParameter!! to it)
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionReceiver?.let {
|
||||||
|
res += (descriptor.extensionReceiverParameter!! to it)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.valueParameters.forEach {
|
||||||
|
val arg = getValueArgument(it.index)
|
||||||
|
if (arg != null) {
|
||||||
|
res += (it to arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets arguments that are specified by given mapping of parameters.
|
||||||
|
*/
|
||||||
|
internal fun IrMemberAccessExpression.addArguments(args: Map<ParameterDescriptor, IrExpression>) {
|
||||||
|
descriptor.dispatchReceiverParameter?.let {
|
||||||
|
val arg = args[it]
|
||||||
|
if (arg != null) {
|
||||||
|
this.dispatchReceiver = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.extensionReceiverParameter?.let {
|
||||||
|
val arg = args[it]
|
||||||
|
if (arg != null) {
|
||||||
|
this.extensionReceiver = arg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.valueParameters.forEach {
|
||||||
|
val arg = args[it]
|
||||||
|
if (arg != null) {
|
||||||
|
this.putValueArgument(it.index, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IrMemberAccessExpression.addArguments(args: List<Pair<ParameterDescriptor, IrExpression>>) =
|
||||||
|
this.addArguments(args.toMap())
|
||||||
+2
@@ -3,6 +3,7 @@ package org.jetbrains.kotlin.backend.konan
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.LocalFunctionsLowering
|
import org.jetbrains.kotlin.backend.common.lower.LocalFunctionsLowering
|
||||||
import org.jetbrains.kotlin.backend.common.lower.SharedVariablesLowering
|
import org.jetbrains.kotlin.backend.common.lower.SharedVariablesLowering
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
|
import org.jetbrains.kotlin.backend.konan.lower.CallableReferenceLowering
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
|
|
||||||
@@ -17,5 +18,6 @@ internal class KonanLower(val context: KonanBackendContext) {
|
|||||||
fun lower(irFile: IrFile) {
|
fun lower(irFile: IrFile) {
|
||||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||||
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
|
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
|
||||||
|
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-27
@@ -1530,39 +1530,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the arguments of [expression] explicitly represented in the IR to the parameters of accessed function.
|
|
||||||
* The arguments should be further evaluated in the same order as they appear in the resulting list.
|
|
||||||
*/
|
|
||||||
private fun bindExplicitArgs(expression: IrMemberAccessExpression): List<Pair<ParameterDescriptor, IrExpression>> {
|
|
||||||
val res = mutableListOf<Pair<ParameterDescriptor, IrExpression>>()
|
|
||||||
val descriptor = expression.descriptor
|
|
||||||
|
|
||||||
// TODO: ensure the order below corresponds to the one defined in Kotlin specs.
|
|
||||||
|
|
||||||
expression.dispatchReceiver?.let {
|
|
||||||
res += (descriptor.dispatchReceiverParameter!! to it)
|
|
||||||
}
|
|
||||||
|
|
||||||
expression.extensionReceiver?.let {
|
|
||||||
res += (descriptor.extensionReceiverParameter!! to it)
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.valueParameters.map {
|
|
||||||
//println(it.index)
|
|
||||||
res += (it to expression.getValueArgument(it.index)!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates all arguments of [expression] that are explicitly represented in the IR.
|
* Evaluates all arguments of [expression] that are explicitly represented in the IR.
|
||||||
* Returns results in the same order as LLVM function expects, assuming that all explicit arguments
|
* Returns results in the same order as LLVM function expects, assuming that all explicit arguments
|
||||||
* exactly correspond to a tail of LLVM parameters.
|
* exactly correspond to a tail of LLVM parameters.
|
||||||
*/
|
*/
|
||||||
private fun evaluateExplicitArgs(expression: IrMemberAccessExpression): List<LLVMValueRef> {
|
private fun evaluateExplicitArgs(expression: IrMemberAccessExpression): List<LLVMValueRef> {
|
||||||
val evaluatedArgs = bindExplicitArgs(expression).map { (param, argExpr) ->
|
val evaluatedArgs = expression.getArguments().map { (param, argExpr) ->
|
||||||
param to evaluateExpression("arg", argExpr)!!
|
param to evaluateExpression("arg", argExpr)!!
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
|
|||||||
+203
@@ -0,0 +1,203 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lowering pass lowers all [IrCallableReference]s to unbound ones.
|
||||||
|
*/
|
||||||
|
internal class CallableReferenceLowering(val context: KonanBackendContext) : DeclarationContainerLoweringPass {
|
||||||
|
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
|
||||||
|
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
|
||||||
|
if (memberDeclaration is IrFunction)
|
||||||
|
lowerCallableReferences(context, memberDeclaration)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all callable references in the function body to unbound ones.
|
||||||
|
* Returns the list of this function and all created ones.
|
||||||
|
*/
|
||||||
|
private fun lowerCallableReferences(context: KonanBackendContext, function: IrFunction): List<IrFunction> {
|
||||||
|
val transformer = CallableReferencesUnbinder(context, function)
|
||||||
|
function.transformChildrenVoid(transformer)
|
||||||
|
return function.singletonList() + transformer.createdFunctions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all callable references in the function body to unbound ones.
|
||||||
|
* Adds all created functions to [createdFunctions].
|
||||||
|
*/
|
||||||
|
private class CallableReferencesUnbinder(val context: KonanBackendContext,
|
||||||
|
val function: IrFunction) : IrElementTransformerVoid() {
|
||||||
|
|
||||||
|
val createdFunctions = mutableListOf<IrFunction>()
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement): IrElement {
|
||||||
|
return super.visitElement(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val unboundCallableReferenceType by lazy {
|
||||||
|
context.builtIns.getKonanInternalClass("UnboundCallableReference").defaultType
|
||||||
|
}
|
||||||
|
|
||||||
|
private val simpleFunctionImplClass by lazy {
|
||||||
|
context.builtIns.getKonanInternalClass("SimpleFunctionImpl")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCallableReference(expression: IrCallableReference): IrExpression {
|
||||||
|
assert (expression.type.isFunctionType)
|
||||||
|
|
||||||
|
val descriptor = expression.descriptor
|
||||||
|
val boundArgs = expression.getArguments()
|
||||||
|
val boundParams = boundArgs.map { it.first }
|
||||||
|
val unboundParams = descriptor.allValueParameters - boundParams
|
||||||
|
|
||||||
|
val startOffset = expression.startOffset
|
||||||
|
val endOffset = expression.endOffset
|
||||||
|
|
||||||
|
// The code below creates a call to `SimpleFunctionImpl` constructor which creates the value to be used
|
||||||
|
// as required callable reference.
|
||||||
|
|
||||||
|
// Create the function to be used as `unboundRef` of `SimpleFunctionImpl`:
|
||||||
|
val newFunction = createSimpleFunctionImplTarget(descriptor, unboundParams, boundParams, startOffset, endOffset)
|
||||||
|
|
||||||
|
createdFunctions.add(newFunction)
|
||||||
|
|
||||||
|
// Create the call to constructor and its arguments:
|
||||||
|
|
||||||
|
val unboundRef = IrCallableReferenceImpl(startOffset, endOffset,
|
||||||
|
unboundCallableReferenceType, newFunction.descriptor, null)
|
||||||
|
val simpleFunctionImplClassConstructor = simpleFunctionImplClass.unsubstitutedPrimaryConstructor!!
|
||||||
|
|
||||||
|
val boundArgsVarargParam = simpleFunctionImplClassConstructor.valueParameters[1]
|
||||||
|
val boundArgsVararg = IrVarargImpl(
|
||||||
|
startOffset, endOffset,
|
||||||
|
boundArgsVarargParam.type,
|
||||||
|
boundArgsVarargParam.varargElementType!!,
|
||||||
|
boundArgs.map { it.second }
|
||||||
|
)
|
||||||
|
|
||||||
|
return IrCallImpl(startOffset, endOffset, simpleFunctionImplClassConstructor).apply {
|
||||||
|
putValueArgument(0, unboundRef)
|
||||||
|
putValueArgument(1, boundArgsVararg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For given function [descriptor] creates the function which calls [descriptor] but
|
||||||
|
* takes all [boundParams] packed into `SimpleFunctionImpl` (and all [unboundParams] as is).
|
||||||
|
*/
|
||||||
|
private fun createSimpleFunctionImplTarget(descriptor: CallableDescriptor,
|
||||||
|
unboundParams: List<ParameterDescriptor>,
|
||||||
|
boundParams: List<ParameterDescriptor>,
|
||||||
|
startOffset: Int, endOffset: Int): IrFunctionImpl {
|
||||||
|
|
||||||
|
// Create new function descriptor:
|
||||||
|
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
|
||||||
|
|
||||||
|
// Create new function declaration:
|
||||||
|
|
||||||
|
val origin = object : IrDeclarationOriginImpl("FUNCTION_FOR_CALLABLE_REFERENCE") {}
|
||||||
|
|
||||||
|
val simpleFunctionImplBoundArgsGetter =
|
||||||
|
simpleFunctionImplClass.defaultType.memberScope.getContributedDescriptors()
|
||||||
|
.filterIsInstance<PropertyDescriptor>().filter { it.name.asString() == "boundArgs" }
|
||||||
|
.single().getter!!
|
||||||
|
|
||||||
|
val boundArgsArray = IrCallImpl(startOffset, endOffset, simpleFunctionImplBoundArgsGetter, null).apply {
|
||||||
|
dispatchReceiver = IrGetValueImpl(startOffset, endOffset, newDescriptor.valueParameters[0])
|
||||||
|
}
|
||||||
|
// TODO: do not call boundArgs getter for each bound argument.
|
||||||
|
|
||||||
|
val arrayGet = boundArgsArray.type.memberScope.getContributedDescriptors()
|
||||||
|
.filterIsInstance<FunctionDescriptor>().filter { it.name.asString() == "get" }.single()
|
||||||
|
|
||||||
|
// TODO: some handling for type parameters is probably required here.
|
||||||
|
|
||||||
|
// Call to old function from new function:
|
||||||
|
val call = IrCallImpl(startOffset, endOffset, descriptor)
|
||||||
|
|
||||||
|
// unboundParams are received as all new function parameters except first:
|
||||||
|
val newUnboundParams = newDescriptor.valueParameters.drop(1)
|
||||||
|
assert (unboundParams.size == newUnboundParams.size)
|
||||||
|
call.addArguments(unboundParams.mapIndexed { index, param ->
|
||||||
|
param to IrGetValueImpl(startOffset, endOffset, newUnboundParams[index])
|
||||||
|
})
|
||||||
|
|
||||||
|
// boundParams are received in `SimpleFunctionImpl.boundArgs`:
|
||||||
|
call.addArguments(boundParams.mapIndexed { index, param ->
|
||||||
|
param to IrCallImpl(startOffset, endOffset, arrayGet).apply {
|
||||||
|
dispatchReceiver = boundArgsArray
|
||||||
|
putValueArgument(0, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, index))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val ret = IrReturnImpl(startOffset, endOffset, newDescriptor, call)
|
||||||
|
|
||||||
|
val newFunction = IrFunctionImpl(
|
||||||
|
startOffset, endOffset, origin,
|
||||||
|
newDescriptor,
|
||||||
|
IrBlockBodyImpl(startOffset, endOffset, listOf(ret))
|
||||||
|
)
|
||||||
|
|
||||||
|
return newFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates descriptor for [createSimpleFunctionImplTarget].
|
||||||
|
*/
|
||||||
|
private fun createSimpleFunctionImplTargetDescriptor(descriptor: CallableDescriptor,
|
||||||
|
unboundParams: List<ParameterDescriptor>): FunctionDescriptor {
|
||||||
|
|
||||||
|
val newName = Name.identifier(descriptor.hashCode().toString()) // FIXME
|
||||||
|
|
||||||
|
val newDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||||
|
function.descriptor.containingDeclaration, Annotations.EMPTY,
|
||||||
|
newName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE)
|
||||||
|
|
||||||
|
val simpleFunctionImplType = simpleFunctionImplClass.defaultType
|
||||||
|
|
||||||
|
// Maps each unbound param of original function to parameter of new function
|
||||||
|
val newUnboundParams = unboundParams.mapIndexed { index, param ->
|
||||||
|
ValueParameterDescriptorImpl(newDescriptor, null, index + 1,
|
||||||
|
Annotations.EMPTY, param.name, param.type,
|
||||||
|
false, false, false, false,
|
||||||
|
(param as? ValueParameterDescriptor)?.varargElementType,
|
||||||
|
SourceElement.NO_SOURCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `<func>: SimpleFunctionImpl`
|
||||||
|
val functionParameter = ValueParameterDescriptorImpl(newDescriptor, null, 0, Annotations.EMPTY,
|
||||||
|
Name.special("<func>"),
|
||||||
|
simpleFunctionImplType, false, false, false, false, null, SourceElement.NO_SOURCE)
|
||||||
|
|
||||||
|
val newValueParameters = listOf(functionParameter) + newUnboundParams
|
||||||
|
|
||||||
|
newDescriptor.initialize(null, null, descriptor.typeParameters, newValueParameters,
|
||||||
|
descriptor.returnType, Modality.FINAL, Visibilities.PRIVATE)
|
||||||
|
|
||||||
|
return newDescriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package konan.internal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the reference to callable without any bound arguments.
|
||||||
|
* It is supposed to be represented at runtime as native pointer to code.
|
||||||
|
*
|
||||||
|
* TODO: it seems to be very closely related to native interop types.
|
||||||
|
*/
|
||||||
|
abstract class UnboundCallableReference internal constructor()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Values of function types are represented at runtime as instances of this class (and its subclasses).
|
||||||
|
*
|
||||||
|
* [FunctionImpl] is a reference to callable with itself as single bound argument,
|
||||||
|
* i.e. when calling this function with arguments `args`, [unboundRef] is called with arguments `(this, *args)`.
|
||||||
|
* So the instance is supposed to contain all implicit (bound) arguments of target function, e.g. captured values.
|
||||||
|
*/
|
||||||
|
open class FunctionImpl(val unboundRef: UnboundCallableReference)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple [FunctionImpl] implementation: all bound arguments of target callable to be placed into [boundArgs].
|
||||||
|
*/
|
||||||
|
class SimpleFunctionImpl(unboundRef: UnboundCallableReference, vararg val boundArgs: Any?) : FunctionImpl(unboundRef)
|
||||||
Reference in New Issue
Block a user