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:
Vasily Levchenko
2017-02-01 16:43:02 +03:00
committed by vvlevchenko
parent bb43b6ab09
commit 2479593f44
@@ -47,17 +47,9 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
typeArguments = expression.descriptor.original.typeParameters.map { it to expression.getTypeArgument(it)!! }.toMap())
originalCall.dispatchReceiver = expression.dispatchReceiver
originalCall.extensionReceiver = expression.extensionReceiver
var parameterIndex = 0
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
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)
}
}
})
functionDescriptor.valueParameters.forEach {
originalCall.putValueArgument(it.index, if (varargToBlock.containsKey(it)) varargToBlock[it] else expression.getValueArgument(it))
}
return originalCall
}
}
@@ -65,78 +57,84 @@ class VarargInjectionLowering internal constructor(val context: Context): Functi
}
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<IrVararg, IrBlock> {
val varargArgs = mutableMapOf<IrVararg, IrBlock>()
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }
expression.acceptVoid(object : IrElementVisitorVoid {
val scope = owner.scope()
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<ValueParameterDescriptor, IrBlock> {
val varargArgs = mutableMapOf<ValueParameterDescriptor, IrBlock>()
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }!!
val scope = owner.scope()
val calleeDescriptor = expression.descriptor
calleeDescriptor.valueParameters
.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) {
offset(expression, scope) {
val hasSpreadElement = hasSpreadElement(expression)
val block = irBlock(kArrayType.defaultType)
if (!hasSpreadElement && expression.elements.all { it is IrConst<*> && KotlinBuiltIns.isString(it.type)}) {
log("skipped vararg expression because it's string array literal")
return
}
if (parameterExpression == null) {
arrayConstructorCall.putValueArgument(0, kIntZero)
block.statements.add(arrayConstructorCall)
varargArgs.put(it, block)
return@forEach
}
val vars = expression.elements.map {
val initVar = scope.createTemporaryVariable((it as? IrSpreadElement)?.expression ?: it as IrExpression, "__elem\$", true)
block.statements.add(initVar)
it to initVar
}.toMap()
val arrayConstructorCall = irCall(
descriptor = arrayConstructor!!,
typeArguments = mapOf(arrayConstructor.typeParameters[0] to expression.varargElementType))
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, expression, vars))
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
block.statements.add(arrayTmpVariable)
if (hasSpreadElement) {
block.statements.add(indexTmpVariable)
}
expression.elements.forEachIndexed { i, element ->
offset(expression, scope) {
log("element:$i> ${ir2string(element)}")
val dst = vars[element]!!
if (element !is IrSpreadElement) {
val setArrayElementCall = irCall(
descriptor = kArraySetFunctionDescriptor,
typeArguments = null
)
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
setArrayElementCall.putValueArgument(0, if (hasSpreadElement) irGet(indexTmpVariable.descriptor) else irConstInt(i))
setArrayElementCall.putValueArgument(1, irGet(dst.descriptor))
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)}")
val vars = parameterExpression.elements.map {
val initVar = scope.createTemporaryVariable((it as? IrSpreadElement)?.expression ?: it as IrExpression, "__elem\$", true)
block.statements.add(initVar)
it to initVar
}.toMap()
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, parameterExpression, vars))
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
block.statements.add(arrayTmpVariable)
if (hasSpreadElement) {
block.statements.add(indexTmpVariable)
}
parameterExpression.elements.forEachIndexed { i, element ->
offset(parameterExpression, scope) {
log("element:$i> ${ir2string(element)}")
val dst = vars[element]!!
if (element !is IrSpreadElement) {
val setArrayElementCall = irCall(
descriptor = kArraySetFunctionDescriptor,
typeArguments = null
)
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
setArrayElementCall.putValueArgument(0, if (hasSpreadElement) irGet(indexTmpVariable.descriptor) else irConstInt(i))
setArrayElementCall.putValueArgument(1, irGet(dst.descriptor))
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)}")
}
}
block.statements.add(irGet(arrayTmpVariable.descriptor))
varargArgs.put(expression, block)
}
block.statements.add(irGet(arrayTmpVariable.descriptor))
varargArgs.put(it, block)
}
})
}
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) {
context.log("VARARG-INJECTOR: $msg")