IR: using function descriptor valueParameters field instead of visiting children of IrCall
code
```
fun foo(vararg x: Any?) {}
fun bar() = foo()
```
produce following IR:
```
FUN public fun bar(): kotlin.Unit
BLOCK_BODY
RETURN type=kotlin.Nothing from='bar(): Unit'
CALL 'foo(vararg Any?): Unit' type=kotlin.Unit origin=null
x: BLOCK type=kotlin.Array<T> origin=null
CALL 'constructor Array(Int)' type=kotlin.Array<T> origin=null
<T>: Any?
size: CONST Int type=kotlin.Int value='0'
```
This commit is contained in:
committed by
vvlevchenko
parent
bb43b6ab09
commit
2479593f44
+75
-77
@@ -47,17 +47,9 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
|||||||
typeArguments = expression.descriptor.original.typeParameters.map { it to expression.getTypeArgument(it)!! }.toMap())
|
typeArguments = expression.descriptor.original.typeParameters.map { it to expression.getTypeArgument(it)!! }.toMap())
|
||||||
originalCall.dispatchReceiver = expression.dispatchReceiver
|
originalCall.dispatchReceiver = expression.dispatchReceiver
|
||||||
originalCall.extensionReceiver = expression.extensionReceiver
|
originalCall.extensionReceiver = expression.extensionReceiver
|
||||||
var parameterIndex = 0
|
functionDescriptor.valueParameters.forEach {
|
||||||
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
|
originalCall.putValueArgument(it.index, if (varargToBlock.containsKey(it)) varargToBlock[it] else expression.getValueArgument(it))
|
||||||
override fun visitElement(element: IrElement) {
|
}
|
||||||
offset(expression, scope) {
|
|
||||||
val parameter = if (element is IrVararg && varargToBlock.containsKey(element))
|
|
||||||
varargToBlock[element]!!
|
|
||||||
else element
|
|
||||||
originalCall.putValueArgument(parameterIndex++, parameter as IrExpression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return originalCall
|
return originalCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,78 +57,84 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<IrVararg, IrBlock> {
|
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<ValueParameterDescriptor, IrBlock> {
|
||||||
val varargArgs = mutableMapOf<IrVararg, IrBlock>()
|
val varargArgs = mutableMapOf<ValueParameterDescriptor, IrBlock>()
|
||||||
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }
|
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }!!
|
||||||
expression.acceptVoid(object : IrElementVisitorVoid {
|
val scope = owner.scope()
|
||||||
val scope = owner.scope()
|
val calleeDescriptor = expression.descriptor
|
||||||
override fun visitElement(element: IrElement) {
|
calleeDescriptor.valueParameters
|
||||||
element.acceptChildrenVoid(this)
|
.filter{ it.varargElementType != null && expression.getValueArgument(it) is IrVararg?}
|
||||||
}
|
.forEach {
|
||||||
|
val type = it.varargElementType!!
|
||||||
|
val parameterExpression = expression.getValueArgument(it) as IrVararg?
|
||||||
|
offset(expression, scope) {
|
||||||
|
val hasSpreadElement = hasSpreadElement(parameterExpression)
|
||||||
|
if (!hasSpreadElement && parameterExpression?.elements?.all { it is IrConst<*> && KotlinBuiltIns.isString(it.type)}?:false) {
|
||||||
|
log("skipped vararg expression because it's string array literal")
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
val block = irBlock(kArrayType.defaultType)
|
||||||
|
val arrayConstructorCall = irCall(
|
||||||
|
descriptor = arrayConstructor,
|
||||||
|
typeArguments = mapOf(arrayConstructor.typeParameters[0] to type))
|
||||||
|
|
||||||
override fun visitVararg(expression: IrVararg) {
|
if (parameterExpression == null) {
|
||||||
offset(expression, scope) {
|
arrayConstructorCall.putValueArgument(0, kIntZero)
|
||||||
val hasSpreadElement = hasSpreadElement(expression)
|
block.statements.add(arrayConstructorCall)
|
||||||
val block = irBlock(kArrayType.defaultType)
|
varargArgs.put(it, block)
|
||||||
if (!hasSpreadElement && expression.elements.all { it is IrConst<*> && KotlinBuiltIns.isString(it.type)}) {
|
return@forEach
|
||||||
log("skipped vararg expression because it's string array literal")
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val vars = expression.elements.map {
|
val vars = parameterExpression.elements.map {
|
||||||
val initVar = scope.createTemporaryVariable((it as? IrSpreadElement)?.expression ?: it as IrExpression, "__elem\$", true)
|
val initVar = scope.createTemporaryVariable((it as? IrSpreadElement)?.expression ?: it as IrExpression, "__elem\$", true)
|
||||||
block.statements.add(initVar)
|
block.statements.add(initVar)
|
||||||
it to initVar
|
it to initVar
|
||||||
}.toMap()
|
}.toMap()
|
||||||
val arrayConstructorCall = irCall(
|
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, parameterExpression, vars))
|
||||||
descriptor = arrayConstructor!!,
|
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
|
||||||
typeArguments = mapOf(arrayConstructor.typeParameters[0] to expression.varargElementType))
|
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
|
||||||
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, expression, vars))
|
block.statements.add(arrayTmpVariable)
|
||||||
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
|
if (hasSpreadElement) {
|
||||||
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
|
block.statements.add(indexTmpVariable)
|
||||||
block.statements.add(arrayTmpVariable)
|
}
|
||||||
if (hasSpreadElement) {
|
parameterExpression.elements.forEachIndexed { i, element ->
|
||||||
block.statements.add(indexTmpVariable)
|
offset(parameterExpression, scope) {
|
||||||
}
|
log("element:$i> ${ir2string(element)}")
|
||||||
expression.elements.forEachIndexed { i, element ->
|
val dst = vars[element]!!
|
||||||
offset(expression, scope) {
|
if (element !is IrSpreadElement) {
|
||||||
log("element:$i> ${ir2string(element)}")
|
val setArrayElementCall = irCall(
|
||||||
val dst = vars[element]!!
|
descriptor = kArraySetFunctionDescriptor,
|
||||||
if (element !is IrSpreadElement) {
|
typeArguments = null
|
||||||
val setArrayElementCall = irCall(
|
)
|
||||||
descriptor = kArraySetFunctionDescriptor,
|
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
|
||||||
typeArguments = null
|
setArrayElementCall.putValueArgument(0, if (hasSpreadElement) irGet(indexTmpVariable.descriptor) else irConstInt(i))
|
||||||
)
|
setArrayElementCall.putValueArgument(1, irGet(dst.descriptor))
|
||||||
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
|
block.statements.add(setArrayElementCall)
|
||||||
setArrayElementCall.putValueArgument(0, if (hasSpreadElement) irGet(indexTmpVariable.descriptor) else irConstInt(i))
|
if (hasSpreadElement) {
|
||||||
setArrayElementCall.putValueArgument(1, irGet(dst.descriptor))
|
block.statements.add(incrementVariable(indexTmpVariable.descriptor, kIntOne))
|
||||||
block.statements.add(setArrayElementCall)
|
|
||||||
if (hasSpreadElement) {
|
|
||||||
block.statements.add(incrementVariable(indexTmpVariable.descriptor, kIntOne))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val arraySizeVariable = scope.createTemporaryVariable(irArraySize(irGet(dst.descriptor)), "__length\$")
|
|
||||||
block.statements.add(arraySizeVariable)
|
|
||||||
val copyCall = irCall(kCopyRangeToDescriptor, null).apply {
|
|
||||||
extensionReceiver = irGet(dst.descriptor)
|
|
||||||
putValueArgument(0, irGet(arrayTmpVariable.descriptor)) /* destination */
|
|
||||||
putValueArgument(1, kIntZero) /* fromIndex */
|
|
||||||
putValueArgument(2, irGet(arraySizeVariable.descriptor)) /* toIndex */
|
|
||||||
putValueArgument(3, irGet(indexTmpVariable.descriptor)) /* destinationIndex */
|
|
||||||
}
|
|
||||||
block.statements.add(copyCall)
|
|
||||||
block.statements.add(incrementVariable(indexTmpVariable.descriptor,
|
|
||||||
irGet(arraySizeVariable.descriptor)))
|
|
||||||
log("element:$i:spread element> ${ir2string(element.expression)}")
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
val arraySizeVariable = scope.createTemporaryVariable(irArraySize(irGet(dst.descriptor)), "__length\$")
|
||||||
|
block.statements.add(arraySizeVariable)
|
||||||
|
val copyCall = irCall(kCopyRangeToDescriptor, null).apply {
|
||||||
|
extensionReceiver = irGet(dst.descriptor)
|
||||||
|
putValueArgument(0, irGet(arrayTmpVariable.descriptor)) /* destination */
|
||||||
|
putValueArgument(1, kIntZero) /* fromIndex */
|
||||||
|
putValueArgument(2, irGet(arraySizeVariable.descriptor)) /* toIndex */
|
||||||
|
putValueArgument(3, irGet(indexTmpVariable.descriptor)) /* destinationIndex */
|
||||||
|
}
|
||||||
|
block.statements.add(copyCall)
|
||||||
|
block.statements.add(incrementVariable(indexTmpVariable.descriptor,
|
||||||
|
irGet(arraySizeVariable.descriptor)))
|
||||||
|
log("element:$i:spread element> ${ir2string(element.expression)}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block.statements.add(irGet(arrayTmpVariable.descriptor))
|
|
||||||
varargArgs.put(expression, block)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block.statements.add(irGet(arrayTmpVariable.descriptor))
|
||||||
|
varargArgs.put(it, block)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
return varargArgs
|
return varargArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +174,7 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun hasSpreadElement(expression: IrVararg) = expression.elements.any { it is IrSpreadElement }
|
private fun hasSpreadElement(expression: IrVararg?) = expression?.elements?.any { it is IrSpreadElement }?:false
|
||||||
|
|
||||||
private fun log(msg:String) {
|
private fun log(msg:String) {
|
||||||
context.log("VARARG-INJECTOR: $msg")
|
context.log("VARARG-INJECTOR: $msg")
|
||||||
|
|||||||
Reference in New Issue
Block a user